Windows Support (#1159)
This commit is contained in:
parent
a49b023a28
commit
0449ec1868
33
.github/workflows/ci-go-test-windows.yml
vendored
Normal file
33
.github/workflows/ci-go-test-windows.yml
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
name: tests-windows
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'mkdocs.yml'
|
||||
- 'README.md'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'mkdocs.yml'
|
||||
- 'README.md'
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- name: Set up Go 1.17
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.17
|
||||
id: go
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
- name: Build
|
||||
run: make build && go get -u github.com/jandelgado/gcov2lcov
|
||||
- name: All tests
|
||||
run: go test -coverprofile coverage.out -covermode=atomic ./...
|
37
.github/workflows/ci-windows-build-msi.yml
vendored
Normal file
37
.github/workflows/ci-windows-build-msi.yml
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
name: build-msi
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'mkdocs.yml'
|
||||
- 'README.md'
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- name: Set up Go 1.17
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.17
|
||||
id: go
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
- id: get_latest_release
|
||||
uses: pozetroninc/github-action-get-latest-release@master
|
||||
with:
|
||||
repository: crowdsecurity/crowdsec
|
||||
excludes: draft
|
||||
- id: set_release_in_env
|
||||
run: echo "BUILD_VERSION=${{ steps.get_latest_release.outputs.release }}" >> $env:GITHUB_ENV
|
||||
- name: Build
|
||||
run: make windows_installer
|
||||
- name: Upload MSI
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: crowdsec*msi
|
||||
name: crowdsec.msi
|
32
.github/workflows/ci_golangci-lint-windows.yml
vendored
Normal file
32
.github/workflows/ci_golangci-lint-windows.yml
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
name: golangci-lint-windows
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'mkdocs.yml'
|
||||
- 'README.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'mkdocs.yml'
|
||||
- 'README.md'
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint-windows
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||
version: v1.45.2
|
||||
# Optional: golangci-lint command line arguments.
|
||||
args: --issues-exit-code=0 --timeout 5m
|
||||
only-new-issues: true
|
||||
|
||||
|
11
.gitignore
vendored
11
.gitignore
vendored
|
@ -29,3 +29,14 @@ plugins/notifications/slack/notification-slack
|
|||
plugins/notifications/splunk/notification-splunk
|
||||
plugins/notifications/email/notification-email
|
||||
plugins/notifications/dummy/notification-dummy
|
||||
|
||||
#test binaries
|
||||
pkg/csplugin/tests/cs_plugin_test*
|
||||
|
||||
#release stuff
|
||||
crowdsec-v*
|
||||
pkg/cwhub/hubdir/.index.json
|
||||
msi
|
||||
*.msi
|
||||
*.nukpg
|
||||
*.tgz
|
||||
|
|
184
Makefile
184
Makefile
|
@ -1,4 +1,13 @@
|
|||
ifeq ($(OS),Windows_NT)
|
||||
SHELL := pwsh.exe
|
||||
.SHELLFLAGS := -NoProfile -Command
|
||||
ROOT= $(shell (Get-Location).Path)
|
||||
SYSTEM=windows
|
||||
EXT=.exe
|
||||
else
|
||||
ROOT?= $(shell pwd)
|
||||
SYSTEM?= $(shell uname -s | tr '[A-Z]' '[a-z]')
|
||||
endif
|
||||
|
||||
ifneq ("$(wildcard $(CURDIR)/platform/$(SYSTEM).mk)", "")
|
||||
include $(CURDIR)/platform/$(SYSTEM).mk
|
||||
|
@ -6,58 +15,50 @@ else
|
|||
include $(CURDIR)/platform/linux.mk
|
||||
endif
|
||||
|
||||
CROWDSEC_FOLDER = "./cmd/crowdsec"
|
||||
CSCLI_FOLDER = "./cmd/crowdsec-cli/"
|
||||
ifneq ($(OS),Windows_NT)
|
||||
include $(ROOT)/platform/unix_common.mk
|
||||
endif
|
||||
|
||||
HTTP_PLUGIN_FOLDER = "./plugins/notifications/http"
|
||||
SLACK_PLUGIN_FOLDER = "./plugins/notifications/slack"
|
||||
SPLUNK_PLUGIN_FOLDER = "./plugins/notifications/splunk"
|
||||
EMAIL_PLUGIN_FOLDER = "./plugins/notifications/email"
|
||||
DUMMY_PLUGIN_FOLDER = "./plugins/notifications/dummy"
|
||||
CROWDSEC_FOLDER = ./cmd/crowdsec
|
||||
CSCLI_FOLDER = ./cmd/crowdsec-cli/
|
||||
|
||||
HTTP_PLUGIN_BIN = "notification-http"
|
||||
SLACK_PLUGIN_BIN = "notification-slack"
|
||||
SPLUNK_PLUGIN_BIN = "notification-splunk"
|
||||
EMAIL_PLUGIN_BIN = "notification-email"
|
||||
DUMMY_PLUGIN_BIN= "notification-dummy"
|
||||
HTTP_PLUGIN_FOLDER = ./plugins/notifications/http
|
||||
SLACK_PLUGIN_FOLDER = ./plugins/notifications/slack
|
||||
SPLUNK_PLUGIN_FOLDER = ./plugins/notifications/splunk
|
||||
EMAIL_PLUGIN_FOLDER = ./plugins/notifications/email
|
||||
DUMMY_PLUGIN_FOLDER = ./plugins/notifications/dummy
|
||||
|
||||
HTTP_PLUGIN_CONFIG = "http.yaml"
|
||||
SLACK_PLUGIN_CONFIG = "slack.yaml"
|
||||
SPLUNK_PLUGIN_CONFIG = "splunk.yaml"
|
||||
EMAIL_PLUGIN_CONFIG = "email.yaml"
|
||||
HTTP_PLUGIN_BIN = notification-http$(EXT)
|
||||
SLACK_PLUGIN_BIN = notification-slack$(EXT)
|
||||
SPLUNK_PLUGIN_BIN = notification-splunk$(EXT)
|
||||
EMAIL_PLUGIN_BIN = notification-email$(EXT)
|
||||
DUMMY_PLUGIN_BIN= notification-dummy$(EXT)
|
||||
|
||||
CROWDSEC_BIN = "crowdsec"
|
||||
CSCLI_BIN = "cscli"
|
||||
BUILD_CMD = "build"
|
||||
HTTP_PLUGIN_CONFIG = http.yaml
|
||||
SLACK_PLUGIN_CONFIG = slack.yaml
|
||||
SPLUNK_PLUGIN_CONFIG = splunk.yaml
|
||||
EMAIL_PLUGIN_CONFIG = email.yaml
|
||||
|
||||
CROWDSEC_BIN = crowdsec$(EXT)
|
||||
CSCLI_BIN = cscli$(EXT)
|
||||
BUILD_CMD = build
|
||||
|
||||
GOOS ?= $(shell go env GOOS)
|
||||
GOARCH ?= $(shell go env GOARCH)
|
||||
|
||||
# Golang version info
|
||||
GO_MAJOR_VERSION = $(shell go version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1)
|
||||
GO_MINOR_VERSION = $(shell go version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2)
|
||||
MINIMUM_SUPPORTED_GO_MAJOR_VERSION = 1
|
||||
MINIMUM_SUPPORTED_GO_MINOR_VERSION = 17
|
||||
GO_VERSION_VALIDATION_ERR_MSG = Golang version ($(BUILD_GOVERSION)) is not supported, please use at least $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION).$(MINIMUM_SUPPORTED_GO_MINOR_VERSION)
|
||||
|
||||
# Current versioning information from env
|
||||
BUILD_VERSION ?= "$(shell git describe --tags)"
|
||||
BUILD_GOVERSION = "$(shell go version | cut -d " " -f3 | sed -E 's/[go]+//g')"
|
||||
BUILD_CODENAME = "alphaga"
|
||||
BUILD_TIMESTAMP = $(shell date +%F"_"%T)
|
||||
BUILD_TAG ?= "$(shell git rev-parse HEAD)"
|
||||
DEFAULT_CONFIGDIR ?= "/etc/crowdsec"
|
||||
DEFAULT_DATADIR ?= "/var/lib/crowdsec/data"
|
||||
BINCOVER_TESTING ?= false
|
||||
|
||||
LD_OPTS_VARS= \
|
||||
-X github.com/crowdsecurity/crowdsec/cmd/crowdsec/main.bincoverTesting=$(BINCOVER_TESTING) \
|
||||
-X github.com/crowdsecurity/crowdsec/pkg/cwversion.Version=$(BUILD_VERSION) \
|
||||
-X github.com/crowdsecurity/crowdsec/pkg/cwversion.BuildDate=$(BUILD_TIMESTAMP) \
|
||||
-X github.com/crowdsecurity/crowdsec/pkg/cwversion.Codename=$(BUILD_CODENAME) \
|
||||
-X github.com/crowdsecurity/crowdsec/pkg/cwversion.Tag=$(BUILD_TAG) \
|
||||
-X github.com/crowdsecurity/crowdsec/pkg/csconfig.defaultConfigDir=$(DEFAULT_CONFIGDIR) \
|
||||
-X github.com/crowdsecurity/crowdsec/pkg/csconfig.defaultDataDir=$(DEFAULT_DATADIR)
|
||||
-X github.com/crowdsecurity/crowdsec/pkg/cwversion.GoVersion=$(BUILD_GOVERSION) \
|
||||
-X 'github.com/crowdsecurity/crowdsec/pkg/csconfig.defaultConfigDir=$(DEFAULT_CONFIGDIR)' \
|
||||
-X 'github.com/crowdsecurity/crowdsec/pkg/csconfig.defaultDataDir=$(DEFAULT_DATADIR)'
|
||||
|
||||
export LD_OPTS=-ldflags "-s -w $(LD_OPTS_VARS)"
|
||||
export LD_OPTS_STATIC=-ldflags "-s -w $(LD_OPTS_VARS) -extldflags '-static'"
|
||||
|
@ -82,6 +83,7 @@ plugins: http-plugin slack-plugin splunk-plugin email-plugin dummy-plugin
|
|||
plugins_static: http-plugin_static slack-plugin_static splunk-plugin_static email-plugin_static dummy-plugin_static
|
||||
|
||||
goversion:
|
||||
ifneq ($(OS),Windows_NT)
|
||||
@if [ $(GO_MAJOR_VERSION) -gt $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION) ]; then \
|
||||
exit 0 ;\
|
||||
elif [ $(GO_MAJOR_VERSION) -lt $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION) ]; then \
|
||||
|
@ -91,105 +93,110 @@ goversion:
|
|||
echo '$(GO_VERSION_VALIDATION_ERR_MSG)';\
|
||||
exit 1; \
|
||||
fi
|
||||
else
|
||||
#This needs Set-ExecutionPolicy -Scope CurrentUser Unrestricted
|
||||
@$(ROOT)/scripts/check_go_version.ps1 $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION) $(MINIMUM_SUPPORTED_GO_MINOR_VERSION)
|
||||
endif
|
||||
|
||||
.PHONY: clean
|
||||
clean: testclean
|
||||
@$(MAKE) -C $(CROWDSEC_FOLDER) clean --no-print-directory
|
||||
@$(MAKE) -C $(CSCLI_FOLDER) clean --no-print-directory
|
||||
@$(RM) $(CROWDSEC_BIN)
|
||||
@$(RM) $(CSCLI_BIN)
|
||||
@$(RM) *.log
|
||||
@$(RM) crowdsec-release.tgz
|
||||
@$(RM) crowdsec-release-static.tgz
|
||||
@$(RM) $(HTTP_PLUGIN_FOLDER)/$(HTTP_PLUGIN_BIN)
|
||||
@$(RM) $(SLACK_PLUGIN_FOLDER)/$(SLACK_PLUGIN_BIN)
|
||||
@$(RM) $(SPLUNK_PLUGIN_FOLDER)/$(SPLUNK_PLUGIN_BIN)
|
||||
@$(RM) $(EMAIL_PLUGIN_FOLDER)/$(EMAIL_PLUGIN_BIN)
|
||||
@$(RM) $(DUMMY_PLUGIN_FOLDER)/$(DUMMY_PLUGIN_BIN)
|
||||
@$(MAKE) -C $(CROWDSEC_FOLDER) clean --no-print-directory RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
|
||||
@$(MAKE) -C $(CSCLI_FOLDER) clean --no-print-directory RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
|
||||
@$(RM) $(CROWDSEC_BIN) $(WIN_IGNORE_ERR)
|
||||
@$(RM) $(CSCLI_BIN) $(WIN_IGNORE_ERR)
|
||||
@$(RM) *.log $(WIN_IGNORE_ERR)
|
||||
@$(RM) crowdsec-release.tgz $(WIN_IGNORE_ERR)
|
||||
@$(RM) crowdsec-release-static.tgz $(WIN_IGNORE_ERR)
|
||||
@$(RM) $(HTTP_PLUGIN_FOLDER)/$(HTTP_PLUGIN_BIN) $(WIN_IGNORE_ERR)
|
||||
@$(RM) $(SLACK_PLUGIN_FOLDER)/$(SLACK_PLUGIN_BIN) $(WIN_IGNORE_ERR)
|
||||
@$(RM) $(SPLUNK_PLUGIN_FOLDER)/$(SPLUNK_PLUGIN_BIN) $(WIN_IGNORE_ERR)
|
||||
@$(RM) $(EMAIL_PLUGIN_FOLDER)/$(EMAIL_PLUGIN_BIN) $(WIN_IGNORE_ERR)
|
||||
@$(RM) $(DUMMY_PLUGIN_FOLDER)/$(DUMMY_PLUGIN_BIN) $(WIN_IGNORE_ERR)
|
||||
|
||||
|
||||
cscli: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(CSCLI_FOLDER) build --no-print-directory
|
||||
@$(MAKE) -C $(CSCLI_FOLDER) build --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
|
||||
|
||||
cscli-bincover: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(CSCLI_FOLDER) build-bincover --no-print-directory
|
||||
|
||||
crowdsec: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(CROWDSEC_FOLDER) build --no-print-directory
|
||||
@$(MAKE) -C $(CROWDSEC_FOLDER) build --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
|
||||
|
||||
crowdsec-bincover: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(CROWDSEC_FOLDER) build-bincover --no-print-directory
|
||||
|
||||
http-plugin: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(HTTP_PLUGIN_FOLDER) build --no-print-directory
|
||||
@$(MAKE) -C $(HTTP_PLUGIN_FOLDER) build --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
|
||||
|
||||
slack-plugin: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(SLACK_PLUGIN_FOLDER) build --no-print-directory
|
||||
@$(MAKE) -C $(SLACK_PLUGIN_FOLDER) build --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
|
||||
|
||||
splunk-plugin: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(SPLUNK_PLUGIN_FOLDER) build --no-print-directory
|
||||
@$(MAKE) -C $(SPLUNK_PLUGIN_FOLDER) build --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
|
||||
|
||||
email-plugin: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(EMAIL_PLUGIN_FOLDER) build --no-print-directory
|
||||
@$(MAKE) -C $(EMAIL_PLUGIN_FOLDER) build --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
|
||||
|
||||
dummy-plugin: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(DUMMY_PLUGIN_FOLDER) build --no-print-directory
|
||||
$(MAKE) -C $(DUMMY_PLUGIN_FOLDER) build --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
|
||||
|
||||
cscli_static: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(CSCLI_FOLDER) static --no-print-directory
|
||||
@$(MAKE) -C $(CSCLI_FOLDER) static --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
|
||||
|
||||
crowdsec_static: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(CROWDSEC_FOLDER) static --no-print-directory
|
||||
@$(MAKE) -C $(CROWDSEC_FOLDER) static --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
|
||||
|
||||
http-plugin_static: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(HTTP_PLUGIN_FOLDER) static --no-print-directory
|
||||
@$(MAKE) -C $(HTTP_PLUGIN_FOLDER) static --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
|
||||
|
||||
slack-plugin_static: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(SLACK_PLUGIN_FOLDER) static --no-print-directory
|
||||
@$(MAKE) -C $(SLACK_PLUGIN_FOLDER) static --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
|
||||
|
||||
splunk-plugin_static:goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(SPLUNK_PLUGIN_FOLDER) static --no-print-directory
|
||||
@$(MAKE) -C $(SPLUNK_PLUGIN_FOLDER) static --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
|
||||
|
||||
email-plugin_static:goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(EMAIL_PLUGIN_FOLDER) static --no-print-directory
|
||||
@$(MAKE) -C $(EMAIL_PLUGIN_FOLDER) static --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
|
||||
|
||||
dummy-plugin_static:goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(DUMMY_PLUGIN_FOLDER) static --no-print-directory
|
||||
$(MAKE) -C $(DUMMY_PLUGIN_FOLDER) static --no-print-directory GOARCH=$(GOARCH) GOOS=$(GOOS) RM="$(RM)" WIN_IGNORE_ERR="$(WIN_IGNORE_ERR)" CP="$(CP)" CPR="$(CPR)" MKDIR="$(MKDIR)"
|
||||
|
||||
.PHONY: testclean
|
||||
testclean: bats-clean
|
||||
@$(RM) pkg/apiserver/ent
|
||||
@$(RM) -r pkg/cwhub/hubdir
|
||||
@$(RM) pkg/apiserver/ent $(WIN_IGNORE_ERR)
|
||||
@$(RM) pkg/cwhub/hubdir $(WIN_IGNORE_ERR)
|
||||
|
||||
.PHONY: test
|
||||
test: goversion
|
||||
$(GOTEST) $(LD_OPTS) ./...
|
||||
|
||||
package-common:
|
||||
@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))
|
||||
@mkdir -p $(RELDIR)/$(subst ./,,$(EMAIL_PLUGIN_FOLDER))
|
||||
@echo "Building Release to dir $(RELDIR)"
|
||||
@$(MKDIR) $(RELDIR)/cmd/crowdsec
|
||||
@$(MKDIR) $(RELDIR)/cmd/crowdsec-cli
|
||||
@$(MKDIR) $(RELDIR)/$(subst ./,,$(HTTP_PLUGIN_FOLDER))
|
||||
@$(MKDIR) $(RELDIR)/$(subst ./,,$(SLACK_PLUGIN_FOLDER))
|
||||
@$(MKDIR) $(RELDIR)/$(subst ./,,$(SPLUNK_PLUGIN_FOLDER))
|
||||
@$(MKDIR) $(RELDIR)/$(subst ./,,$(EMAIL_PLUGIN_FOLDER))
|
||||
|
||||
@cp $(CROWDSEC_FOLDER)/$(CROWDSEC_BIN) $(RELDIR)/cmd/crowdsec
|
||||
@cp $(CSCLI_FOLDER)/$(CSCLI_BIN) $(RELDIR)/cmd/crowdsec-cli
|
||||
@$(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 $(EMAIL_PLUGIN_FOLDER)/$(EMAIL_PLUGIN_BIN) $(RELDIR)/$(subst ./,,$(EMAIL_PLUGIN_FOLDER))
|
||||
@$(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) $(EMAIL_PLUGIN_FOLDER)/$(EMAIL_PLUGIN_BIN) $(RELDIR)/$(subst ./,,$(EMAIL_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 $(EMAIL_PLUGIN_FOLDER)/$(EMAIL_PLUGIN_CONFIG) $(RELDIR)/$(subst ./,,$(EMAIL_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) $(EMAIL_PLUGIN_FOLDER)/$(EMAIL_PLUGIN_CONFIG) $(RELDIR)/$(subst ./,,$(EMAIL_PLUGIN_FOLDER))
|
||||
|
||||
@cp -R ./config $(RELDIR)
|
||||
@cp wizard.sh $(RELDIR)
|
||||
@cp scripts/test_env.sh $(RELDIR)
|
||||
@$(CPR) ./config $(RELDIR)
|
||||
@$(CP) wizard.sh $(RELDIR)
|
||||
@$(CP) scripts/test_env.sh $(RELDIR)
|
||||
@$(CP) scripts/test_env.ps1 $(RELDIR)
|
||||
|
||||
.PHONY: package
|
||||
package: package-common
|
||||
|
@ -200,7 +207,11 @@ package_static: package-common
|
|||
|
||||
.PHONY: check_release
|
||||
check_release:
|
||||
ifneq ($(OS),Windows_NT)
|
||||
@if [ -d $(RELDIR) ]; then echo "$(RELDIR) already exists, abort" ; exit 1 ; fi
|
||||
else
|
||||
@if (Test-Path -Path $(RELDIR)) { echo "$(RELDIR) already exists, abort" ; exit 1 ; }
|
||||
endif
|
||||
|
||||
.PHONY: release
|
||||
release: check_release build package
|
||||
|
@ -208,5 +219,12 @@ release: check_release build package
|
|||
.PHONY: release_static
|
||||
release_static: check_release static package_static
|
||||
|
||||
include tests/bats.mk
|
||||
.PHONY: windows_installer
|
||||
windows_installer: build
|
||||
@.\make_installer.ps1 -version $(BUILD_VERSION)
|
||||
|
||||
.PHONY: chocolatey
|
||||
chocolatey: windows_installer
|
||||
@.\make_chocolatey.ps1 -version $(BUILD_VERSION)
|
||||
|
||||
include tests/bats.mk
|
103
azure-pipelines.yml
Normal file
103
azure-pipelines.yml
Normal file
|
@ -0,0 +1,103 @@
|
|||
trigger:
|
||||
tags:
|
||||
include:
|
||||
- "v*"
|
||||
branches:
|
||||
exclude:
|
||||
- "*"
|
||||
pr: none
|
||||
|
||||
pool:
|
||||
vmImage: windows-latest
|
||||
|
||||
stages:
|
||||
- stage: Build
|
||||
jobs:
|
||||
- job:
|
||||
displayName: "Build"
|
||||
steps:
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: "Install SignClient"
|
||||
inputs:
|
||||
command: 'custom'
|
||||
custom: 'tool'
|
||||
arguments: 'install --global SignClient --version 1.3.155'
|
||||
- task: GoTool@0
|
||||
displayName: "Install Go 1.17"
|
||||
inputs:
|
||||
version: '1.17.9'
|
||||
|
||||
- pwsh: |
|
||||
choco install -y jq
|
||||
choco install -y make
|
||||
displayName: "Install builds deps"
|
||||
- task: PowerShell@2
|
||||
inputs:
|
||||
targetType: 'inline'
|
||||
pwsh: true
|
||||
#we are not calling make windows_installer because we want to sign the binaries before they are added to the MSI
|
||||
script: |
|
||||
make build
|
||||
- task: AzureKeyVault@2
|
||||
inputs:
|
||||
azureSubscription: 'Azure subscription 1(8a93ab40-7e99-445e-ad47-0f6a3e2ef546)'
|
||||
KeyVaultName: 'CodeSigningSecrets'
|
||||
SecretsFilter: 'CodeSigningUser,CodeSigningPassword'
|
||||
RunAsPreJob: false
|
||||
|
||||
- task: DownloadSEcureFile@1
|
||||
inputs:
|
||||
secureFile: appsettings.json
|
||||
|
||||
- pwsh: |
|
||||
SignClient.exe Sign --name "crowdsec-binaries" `
|
||||
--input "**/*.exe" --config (Join-Path -Path $(Agent.TempDirectory) -ChildPath "appsettings.json") `
|
||||
--user $(CodeSigningUser) --secret '$(CodeSigningPassword)'
|
||||
displayName: "Sign Crowdsec binaries + plugins"
|
||||
|
||||
- pwsh: |
|
||||
$build_version=(git describe --tags (git rev-list --tags --max-count=1)).Substring(1)
|
||||
.\make_installer.ps1 -version $build_version
|
||||
Write-Host "##vso[task.setvariable variable=BuildVersion;isOutput=true]$build_version"
|
||||
displayName: "Build Crowdsec MSI"
|
||||
name: BuildMSI
|
||||
|
||||
- pwsh: |
|
||||
SignClient.exe Sign --name "crowdsec-msi" `
|
||||
--input "*.msi" --config (Join-Path -Path $(Agent.TempDirectory) -ChildPath "appsettings.json") `
|
||||
--user $(CodeSigningUser) --secret '$(CodeSigningPassword)'
|
||||
displayName: "Sign Crowdsec MSI"
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.Repository.LocalPath)\\crowdsec_$(BuildMSI.BuildVersion).msi'
|
||||
ArtifactName: 'crowdsec.msi'
|
||||
publishLocation: 'Container'
|
||||
displayName: "Upload MSI artifact"
|
||||
- stage: Publish
|
||||
dependsOn: Build
|
||||
jobs:
|
||||
- deployment: "Publish"
|
||||
displayName: "Publish to GitHub"
|
||||
environment: github
|
||||
strategy:
|
||||
runOnce:
|
||||
deploy:
|
||||
steps:
|
||||
- bash: |
|
||||
tag=$(curl -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/crowdsecurity/crowdsec/releases | jq -r '. | map(select(.prerelease==true)) | sort_by(.created_at) | reverse | .[0].tag_name')
|
||||
echo "##vso[task.setvariable variable=LatestPreRelease;isOutput=true]$tag"
|
||||
name: GetLatestPrelease
|
||||
- task: GitHubRelease@1
|
||||
inputs:
|
||||
gitHubConnection: "github.com_blotus"
|
||||
repositoryName: '$(Build.Repository.Name)'
|
||||
action: 'edit'
|
||||
tag: '$(GetLatestPrelease.LatestPreRelease)'
|
||||
assetUploadMode: 'replace'
|
||||
addChangeLog: false
|
||||
isPreRelease: true #we force prerelease because the pipeline is invoked on tag creation, which happens when we do a prerelease
|
||||
#the .. is an ugly hack, but I can't find the var that gives D:\a\1 ...
|
||||
assets: |
|
||||
$(Build.ArtifactStagingDirectory)\..\crowdsec.msi
|
||||
condition: ne(variables['GetLatestPrelease.LatestPreRelease'], '')
|
|
@ -1,11 +1,17 @@
|
|||
ifeq ($(OS),Windows_NT)
|
||||
SHELL := pwsh.exe
|
||||
.SHELLFLAGS := -NoProfile -Command
|
||||
EXT=.exe
|
||||
endif
|
||||
|
||||
|
||||
# Go parameters
|
||||
GOCMD=go
|
||||
GOBUILD=$(GOCMD) build
|
||||
GOCLEAN=$(GOCMD) clean
|
||||
GOTEST=$(GOCMD) test
|
||||
GOGET=$(GOCMD) get
|
||||
|
||||
BINARY_NAME=cscli
|
||||
BINARY_NAME=cscli$(EXT)
|
||||
# names longer than 15 chars break 'pgrep'
|
||||
BINARY_NAME_COVER=$(BINARY_NAME).cover
|
||||
PREFIX?="/"
|
||||
|
@ -32,8 +38,8 @@ install-bin:
|
|||
@install -v -m 755 -D "$(BINARY_NAME)" "$(BIN_PREFIX)/$(BINARY_NAME)" || exit
|
||||
|
||||
uninstall:
|
||||
@$(RM) -r $(CSCLI_CONFIG)
|
||||
@$(RM) -r $(BIN_PREFIX)$(BINARY_NAME)
|
||||
@$(RM) $(CSCLI_CONFIG) $(WIN_IGNORE_ERR)
|
||||
@$(RM) $(BIN_PREFIX)$(BINARY_NAME) $(WIN_IGNORE_ERR)
|
||||
|
||||
clean:
|
||||
@$(RM) $(BINARY_NAME) $(BINARY_NAME_COVER)
|
||||
@$(RM) $(BINARY_NAME) $(BINARY_NAME_COVER) $(WIN_IGNORE_ERR)
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
func NewCompletionCmd() *cobra.Command {
|
||||
|
||||
var completionCmd = &cobra.Command{
|
||||
Use: "completion [bash|zsh]",
|
||||
Use: "completion [bash|zsh|powershell|fish]",
|
||||
Short: "Generate completion script",
|
||||
Long: `To load completions:
|
||||
|
||||
|
@ -49,10 +49,25 @@ func NewCompletionCmd() *cobra.Command {
|
|||
$ cscli completion zsh > "${fpath[1]}/_cscli"
|
||||
|
||||
# You will need to start a new shell for this setup to take effect.
|
||||
|
||||
### fish:
|
||||
` + "```shell" + `
|
||||
$ cscli completion fish | source
|
||||
|
||||
# To load completions for each session, execute once:
|
||||
$ cscli completion fish > ~/.config/fish/completions/cscli.fish
|
||||
` + "```" + `
|
||||
### PowerShell:
|
||||
` + "```powershell" + `
|
||||
PS> cscli completion powershell | Out-String | Invoke-Expression
|
||||
|
||||
# To load completions for every new session, run:
|
||||
PS> cscli completion powershell > cscli.ps1
|
||||
# and source this file from your PowerShell profile.
|
||||
` + "```",
|
||||
DisableFlagsInUseLine: true,
|
||||
DisableAutoGenTag: true,
|
||||
ValidArgs: []string{"bash", "zsh"},
|
||||
ValidArgs: []string{"bash", "zsh", "powershell", "fish"},
|
||||
Args: cobra.ExactValidArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
switch args[0] {
|
||||
|
@ -60,9 +75,10 @@ func NewCompletionCmd() *cobra.Command {
|
|||
cmd.Root().GenBashCompletion(os.Stdout)
|
||||
case "zsh":
|
||||
cmd.Root().GenZshCompletion(os.Stdout)
|
||||
/*case "fish":
|
||||
case "powershell":
|
||||
cmd.Root().GenPowerShellCompletion(os.Stdout)
|
||||
case "fish":
|
||||
cmd.Root().GenFishCompletion(os.Stdout, true)
|
||||
*/
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
5
cmd/crowdsec-cli/configfile.go
Normal file
5
cmd/crowdsec-cli/configfile.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
// +build linux freebsd netbsd openbsd solaris !windows
|
||||
|
||||
package main
|
||||
|
||||
const DefaultConfigFile = "/etc/crowdsec/config.yaml"
|
6
cmd/crowdsec-cli/configfile_windows.go
Normal file
6
cmd/crowdsec-cli/configfile_windows.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package main
|
||||
|
||||
const DefaultConfigFile = "C:\\ProgramData\\CrowdSec\\config\\config.yaml"
|
|
@ -15,9 +15,12 @@ func ReloadMessage() string {
|
|||
|
||||
var reloadCmd string
|
||||
|
||||
if runtime.GOOS == "freebsd" {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
return "Please restart the crowdsec service for the new configuration to be effective."
|
||||
case "freebsd":
|
||||
reloadCmd = ReloadCmdFreebsd
|
||||
} else {
|
||||
default:
|
||||
reloadCmd = ReloadCmdLinux
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
ifeq ($(OS),Windows_NT)
|
||||
SHELL := pwsh.exe
|
||||
.SHELLFLAGS := -NoProfile -Command
|
||||
EXT=.exe
|
||||
endif
|
||||
|
||||
# Go parameters
|
||||
GOCMD=go
|
||||
GOBUILD=$(GOCMD) build
|
||||
|
@ -5,7 +11,7 @@ GOCLEAN=$(GOCMD) clean
|
|||
GOTEST=$(GOCMD) test
|
||||
GOGET=$(GOCMD) get
|
||||
|
||||
CROWDSEC_BIN=crowdsec
|
||||
CROWDSEC_BIN=crowdsec$(EXT)
|
||||
# names longer than 15 chars break 'pgrep'
|
||||
CROWDSEC_BIN_COVER=$(CROWDSEC_BIN).cover
|
||||
PREFIX?="/"
|
||||
|
@ -32,7 +38,7 @@ test:
|
|||
$(GOTEST) $(LD_OPTS) -v ./...
|
||||
|
||||
clean:
|
||||
@$(RM) $(CROWDSEC_BIN) $(CROWDSEC_BIN).test $(CROWDSEC_BIN_COVER)
|
||||
@$(RM) $(CROWDSEC_BIN) $(CROWDSEC_BIN).test $(CROWDSEC_BIN_COVER) $(WIN_IGNORE_ERR)
|
||||
|
||||
.PHONY: install
|
||||
install: install-conf install-bin
|
||||
|
@ -67,7 +73,7 @@ systemd: install
|
|||
|
||||
.PHONY: uninstall
|
||||
uninstall:
|
||||
$(RM) -r "$(CFG_PREFIX)"
|
||||
$(RM) -r "$(DATA_PREFIX)"
|
||||
$(RM) "$(BIN_PREFIX)/$(CROWDSEC_BIN)"
|
||||
$(RM) "$(SYSTEMD_PATH_FILE)"
|
||||
$(RM) $(CFG_PREFIX) $(WIN_IGNORE_ERR)
|
||||
$(RM) $(DATA_PREFIX) $(WIN_IGNORE_ERR)
|
||||
$(RM) "$(BIN_PREFIX)/$(CROWDSEC_BIN)" $(WIN_IGNORE_ERR)
|
||||
$(RM) "$(SYSTEMD_PATH_FILE)" $(WIN_IGNORE_ERR)
|
||||
|
|
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/apiserver"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
|
@ -18,7 +19,8 @@ func initAPIServer(cConfig *csconfig.Config) (*apiserver.APIServer, error) {
|
|||
|
||||
if hasPlugins(cConfig.API.Server.Profiles) {
|
||||
log.Info("initiating plugin broker")
|
||||
if cConfig.PluginConfig == nil {
|
||||
//On windows, the plugins are always run as medium-integrity processes, so we don't care about plugin_config
|
||||
if cConfig.PluginConfig == nil && runtime.GOOS != "windows" {
|
||||
return nil, fmt.Errorf("plugins are enabled, but the plugin_config section is missing in the configuration")
|
||||
}
|
||||
if cConfig.ConfigPaths.NotificationDir == "" {
|
||||
|
|
5
cmd/crowdsec/configfile.go
Normal file
5
cmd/crowdsec/configfile.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
// +build linux freebsd netbsd openbsd solaris !windows
|
||||
|
||||
package main
|
||||
|
||||
const DefaultConfigFile = "/etc/crowdsec/config.yaml"
|
3
cmd/crowdsec/configfile_windows.go
Normal file
3
cmd/crowdsec/configfile_windows.go
Normal file
|
@ -0,0 +1,3 @@
|
|||
package main
|
||||
|
||||
const DefaultConfigFile = "C:\\ProgramData\\CrowdSec\\config\\config.yaml"
|
|
@ -10,7 +10,6 @@ import (
|
|||
_ "net/http/pprof"
|
||||
"time"
|
||||
|
||||
"github.com/confluentinc/bincover"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/acquisition"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csplugin"
|
||||
|
@ -23,7 +22,6 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/sirupsen/logrus/hooks/writer"
|
||||
|
||||
"gopkg.in/tomb.v2"
|
||||
)
|
||||
|
@ -65,6 +63,7 @@ type Flags struct {
|
|||
TestMode bool
|
||||
DisableAgent bool
|
||||
DisableAPI bool
|
||||
WinSvc string
|
||||
}
|
||||
|
||||
type labelsMap map[string]string
|
||||
|
@ -190,8 +189,8 @@ func (f *Flags) Parse() {
|
|||
flag.BoolVar(&f.TestMode, "t", false, "only test configs")
|
||||
flag.BoolVar(&f.DisableAgent, "no-cs", false, "disable crowdsec agent")
|
||||
flag.BoolVar(&f.DisableAPI, "no-api", false, "disable local API")
|
||||
flag.StringVar(&f.WinSvc, "winsvc", "", "Windows service Action : Install, Remove etc..")
|
||||
flag.StringVar(&dumpFolder, "dump-data", "", "dump parsers/buckets raw outputs")
|
||||
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
|
@ -262,10 +261,6 @@ func LoadConfig(cConfig *csconfig.Config) error {
|
|||
}
|
||||
|
||||
func main() {
|
||||
var (
|
||||
cConfig *csconfig.Config
|
||||
err error
|
||||
)
|
||||
|
||||
defer types.CatchPanic("crowdsec/main")
|
||||
|
||||
|
@ -278,41 +273,5 @@ func main() {
|
|||
cwversion.Show()
|
||||
os.Exit(0)
|
||||
}
|
||||
log.AddHook(&writer.Hook{ // Send logs with level higher than warning to stderr
|
||||
Writer: os.Stderr,
|
||||
LogLevels: []log.Level{
|
||||
log.PanicLevel,
|
||||
log.FatalLevel,
|
||||
},
|
||||
})
|
||||
|
||||
cConfig, err = csconfig.NewConfig(flags.ConfigFile, flags.DisableAgent, flags.DisableAPI)
|
||||
if err != nil {
|
||||
log.Fatalf(err.Error())
|
||||
}
|
||||
if err := LoadConfig(cConfig); err != nil {
|
||||
log.Fatalf(err.Error())
|
||||
}
|
||||
// Configure logging
|
||||
if err = types.SetDefaultLoggerConfig(cConfig.Common.LogMedia, cConfig.Common.LogDir, *cConfig.Common.LogLevel,
|
||||
cConfig.Common.LogMaxSize, cConfig.Common.LogMaxFiles, cConfig.Common.LogMaxAge, cConfig.Common.CompressLogs); err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
log.Infof("Crowdsec %s", cwversion.VersionStr())
|
||||
|
||||
// Enable profiling early
|
||||
if cConfig.Prometheus != nil {
|
||||
go registerPrometheus(cConfig.Prometheus)
|
||||
}
|
||||
|
||||
if exitCode, err := Serve(cConfig); err != nil {
|
||||
if err != nil {
|
||||
log.Errorf(err.Error())
|
||||
if !bincoverTesting {
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
bincover.ExitCode = exitCode
|
||||
}
|
||||
}
|
||||
StartRunSvc()
|
||||
}
|
||||
|
|
61
cmd/crowdsec/run_in_svc.go
Normal file
61
cmd/crowdsec/run_in_svc.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
//go:build linux || freebsd || netbsd || openbsd || solaris || !windows
|
||||
// +build linux freebsd netbsd openbsd solaris !windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/confluentinc/bincover"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/cwversion"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/sirupsen/logrus/hooks/writer"
|
||||
)
|
||||
|
||||
func StartRunSvc() {
|
||||
|
||||
var (
|
||||
cConfig *csconfig.Config
|
||||
err error
|
||||
)
|
||||
|
||||
log.AddHook(&writer.Hook{ // Send logs with level higher than warning to stderr
|
||||
Writer: os.Stderr,
|
||||
LogLevels: []log.Level{
|
||||
log.PanicLevel,
|
||||
log.FatalLevel,
|
||||
},
|
||||
})
|
||||
|
||||
cConfig, err = csconfig.NewConfig(flags.ConfigFile, flags.DisableAgent, flags.DisableAPI)
|
||||
if err != nil {
|
||||
log.Fatalf(err.Error())
|
||||
}
|
||||
if err := LoadConfig(cConfig); err != nil {
|
||||
log.Fatalf(err.Error())
|
||||
}
|
||||
// Configure logging
|
||||
if err = types.SetDefaultLoggerConfig(cConfig.Common.LogMedia, cConfig.Common.LogDir, *cConfig.Common.LogLevel,
|
||||
cConfig.Common.LogMaxSize, cConfig.Common.LogMaxFiles, cConfig.Common.LogMaxAge, cConfig.Common.CompressLogs); err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
log.Infof("Crowdsec %s", cwversion.VersionStr())
|
||||
|
||||
// Enable profiling early
|
||||
if cConfig.Prometheus != nil {
|
||||
go registerPrometheus(cConfig.Prometheus)
|
||||
}
|
||||
|
||||
if exitCode, err := Serve(cConfig); err != nil {
|
||||
if err != nil {
|
||||
log.Errorf(err.Error())
|
||||
if !bincoverTesting {
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
bincover.ExitCode = exitCode
|
||||
}
|
||||
}
|
||||
}
|
101
cmd/crowdsec/run_in_svc_windows.go
Normal file
101
cmd/crowdsec/run_in_svc_windows.go
Normal file
|
@ -0,0 +1,101 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/confluentinc/bincover"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/cwversion"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/sirupsen/logrus/hooks/writer"
|
||||
"golang.org/x/sys/windows/svc"
|
||||
)
|
||||
|
||||
func StartRunSvc() {
|
||||
|
||||
const svcName = "CrowdSec"
|
||||
const svcDescription = "Crowdsec IPS/IDS"
|
||||
|
||||
isRunninginService, err := svc.IsWindowsService()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to determine if we are running in windows service mode: %v", err)
|
||||
}
|
||||
if isRunninginService {
|
||||
runService(svcName)
|
||||
return
|
||||
}
|
||||
|
||||
if flags.WinSvc == "Install" {
|
||||
err = installService(svcName, svcDescription)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to %s %s: %v", flags.WinSvc, svcName, err)
|
||||
}
|
||||
} else if flags.WinSvc == "Remove" {
|
||||
err = removeService(svcName)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to %s %s: %v", flags.WinSvc, svcName, err)
|
||||
}
|
||||
} else if flags.WinSvc == "Start" {
|
||||
err = startService(svcName)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to %s %s: %v", flags.WinSvc, svcName, err)
|
||||
}
|
||||
} else if flags.WinSvc == "Stop" {
|
||||
err = controlService(svcName, svc.Stop, svc.Stopped)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to %s %s: %v", flags.WinSvc, svcName, err)
|
||||
}
|
||||
} else if flags.WinSvc == "" {
|
||||
WindowsRun()
|
||||
} else {
|
||||
log.Fatalf("Invalid value for winsvc parameter: %s", flags.WinSvc)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func WindowsRun() {
|
||||
|
||||
var (
|
||||
cConfig *csconfig.Config
|
||||
err error
|
||||
)
|
||||
|
||||
log.AddHook(&writer.Hook{ // Send logs with level higher than warning to stderr
|
||||
Writer: os.Stderr,
|
||||
LogLevels: []log.Level{
|
||||
log.PanicLevel,
|
||||
log.FatalLevel,
|
||||
},
|
||||
})
|
||||
|
||||
cConfig, err = csconfig.NewConfig(flags.ConfigFile, flags.DisableAgent, flags.DisableAPI)
|
||||
if err != nil {
|
||||
log.Fatalf(err.Error())
|
||||
}
|
||||
if err := LoadConfig(cConfig); err != nil {
|
||||
log.Fatalf(err.Error())
|
||||
}
|
||||
// Configure logging
|
||||
if err = types.SetDefaultLoggerConfig(cConfig.Common.LogMedia, cConfig.Common.LogDir, *cConfig.Common.LogLevel,
|
||||
cConfig.Common.LogMaxSize, cConfig.Common.LogMaxFiles, cConfig.Common.LogMaxAge, cConfig.Common.CompressLogs); err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
log.Infof("Crowdsec %s", cwversion.VersionStr())
|
||||
|
||||
// Enable profiling early
|
||||
if cConfig.Prometheus != nil {
|
||||
go registerPrometheus(cConfig.Prometheus)
|
||||
}
|
||||
|
||||
if exitCode, err := Serve(cConfig); err != nil {
|
||||
if err != nil {
|
||||
log.Errorf(err.Error())
|
||||
if !bincoverTesting {
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
bincover.ExitCode = exitCode
|
||||
}
|
||||
}
|
||||
}
|
|
@ -179,10 +179,11 @@ func shutdown(sig os.Signal, cConfig *csconfig.Config) error {
|
|||
|
||||
func HandleSignals(cConfig *csconfig.Config) int {
|
||||
signalChan := make(chan os.Signal, 1)
|
||||
//We add os.Interrupt mostly to ease windows dev, it allows to simulate a clean shutdown when running in the console
|
||||
signal.Notify(signalChan,
|
||||
syscall.SIGHUP,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGTERM)
|
||||
syscall.SIGTERM,
|
||||
os.Interrupt)
|
||||
|
||||
exitChan := make(chan int)
|
||||
go func() {
|
||||
|
@ -200,7 +201,7 @@ func HandleSignals(cConfig *csconfig.Config) int {
|
|||
log.Fatalf("Reload handler failure : %s", err)
|
||||
}
|
||||
// ctrl+C, kill -SIGINT XXXX, kill -SIGTERM XXXX
|
||||
case syscall.SIGINT, syscall.SIGTERM:
|
||||
case os.Interrupt, syscall.SIGTERM:
|
||||
log.Warningf("SIGTERM received, shutting down")
|
||||
if err := shutdown(s, cConfig); err != nil {
|
||||
log.Fatalf("failed shutdown : %s", err)
|
||||
|
|
87
cmd/crowdsec/win_service.go
Normal file
87
cmd/crowdsec/win_service.go
Normal file
|
@ -0,0 +1,87 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/windows/svc"
|
||||
)
|
||||
|
||||
type crowdsec_winservice struct {
|
||||
config *csconfig.Config
|
||||
}
|
||||
|
||||
func (m *crowdsec_winservice) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
|
||||
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue
|
||||
changes <- svc.Status{State: svc.StartPending}
|
||||
fasttick := time.Tick(500 * time.Millisecond)
|
||||
slowtick := time.Tick(2 * time.Second)
|
||||
tick := fasttick
|
||||
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
|
||||
go WindowsRun()
|
||||
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case <-tick:
|
||||
|
||||
case c := <-r:
|
||||
switch c.Cmd {
|
||||
case svc.Interrogate:
|
||||
changes <- c.CurrentStatus
|
||||
case svc.Stop, svc.Shutdown:
|
||||
changes <- svc.Status{State: svc.StopPending}
|
||||
err := shutdown(nil, m.config)
|
||||
if err != nil {
|
||||
log.Errorf("Error while shutting down: %s", err)
|
||||
//don't return, we still want to notify windows that we are stopped ?
|
||||
}
|
||||
break loop
|
||||
case svc.Pause:
|
||||
changes <- svc.Status{State: svc.Paused, Accepts: cmdsAccepted}
|
||||
tick = slowtick
|
||||
case svc.Continue:
|
||||
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
|
||||
tick = fasttick
|
||||
default:
|
||||
log.Errorf("unexpected control request #%d", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
changes <- svc.Status{State: svc.Stopped}
|
||||
return
|
||||
}
|
||||
|
||||
func runService(name string) {
|
||||
cConfig, err := csconfig.NewConfig(flags.ConfigFile, flags.DisableAgent, flags.DisableAPI)
|
||||
if err != nil {
|
||||
log.Fatalf(err.Error())
|
||||
}
|
||||
if err := LoadConfig(cConfig); err != nil {
|
||||
log.Fatalf(err.Error())
|
||||
}
|
||||
// Configure logging
|
||||
if err = types.SetDefaultLoggerConfig(cConfig.Common.LogMedia, cConfig.Common.LogDir, *cConfig.Common.LogLevel,
|
||||
cConfig.Common.LogMaxSize, cConfig.Common.LogMaxFiles, cConfig.Common.LogMaxAge, cConfig.Common.CompressLogs); err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
log.Infof("starting %s service", name)
|
||||
|
||||
winsvc := crowdsec_winservice{config: cConfig}
|
||||
|
||||
err = svc.Run(name, &winsvc)
|
||||
if err != nil {
|
||||
log.Errorf("%s service failed: %s", name, err)
|
||||
return
|
||||
}
|
||||
log.Infof("%s service stopped", name)
|
||||
}
|
95
cmd/crowdsec/win_service_install.go
Normal file
95
cmd/crowdsec/win_service_install.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/sys/windows/svc/mgr"
|
||||
)
|
||||
|
||||
func exePath() (string, error) {
|
||||
var err error
|
||||
prog := os.Args[0]
|
||||
p, err := filepath.Abs(prog)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fi, err := os.Stat(p)
|
||||
if err == nil {
|
||||
if !fi.Mode().IsDir() {
|
||||
return p, nil
|
||||
}
|
||||
err = fmt.Errorf("%s is directory", p)
|
||||
}
|
||||
if filepath.Ext(p) == "" {
|
||||
var fi os.FileInfo
|
||||
|
||||
p += ".exe"
|
||||
fi, err = os.Stat(p)
|
||||
if err == nil {
|
||||
if !fi.Mode().IsDir() {
|
||||
return p, nil
|
||||
}
|
||||
err = fmt.Errorf("%s is directory", p)
|
||||
}
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
func installService(name, desc string) error {
|
||||
exepath, err := exePath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.Disconnect()
|
||||
s, err := m.OpenService(name)
|
||||
if err == nil {
|
||||
s.Close()
|
||||
return fmt.Errorf("service %s already exists", name)
|
||||
}
|
||||
s, err = m.CreateService(name, exepath, mgr.Config{DisplayName: desc}, "is", "auto-started")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
/*err = eventlog.InstallAsEventCreate(name, eventlog.Error|eventlog.Warning|eventlog.Info)
|
||||
if err != nil {
|
||||
s.Delete()
|
||||
return fmt.Errorf("SetupEventLogSource() failed: %s", err)
|
||||
}*/
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeService(name string) error {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.Disconnect()
|
||||
s, err := m.OpenService(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("service %s is not installed", name)
|
||||
}
|
||||
defer s.Close()
|
||||
err = s.Delete()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
/*err = eventlog.Remove(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("RemoveEventLogSource() failed: %s", err)
|
||||
}*/
|
||||
return nil
|
||||
}
|
64
cmd/crowdsec/win_service_manage.go
Normal file
64
cmd/crowdsec/win_service_manage.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
//"time"
|
||||
|
||||
"golang.org/x/sys/windows/svc"
|
||||
"golang.org/x/sys/windows/svc/mgr"
|
||||
)
|
||||
|
||||
func startService(name string) error {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.Disconnect()
|
||||
s, err := m.OpenService(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not access service: %v", err)
|
||||
}
|
||||
defer s.Close()
|
||||
err = s.Start("is", "manual-started")
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not start service: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func controlService(name string, c svc.Cmd, to svc.State) error {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.Disconnect()
|
||||
s, err := m.OpenService(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not access service: %v", err)
|
||||
}
|
||||
defer s.Close()
|
||||
status, err := s.Control(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not send control=%d: %v", c, err)
|
||||
}
|
||||
timeout := time.Now().Add(10 * time.Second)
|
||||
for status.State != to {
|
||||
if timeout.Before(time.Now()) {
|
||||
return fmt.Errorf("timeout waiting for service to go to state=%d", to)
|
||||
}
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
status, err = s.Query()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not retrieve service status: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
8
config/acquis_win.yaml
Normal file
8
config/acquis_win.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
source: wineventlog
|
||||
event_channel: Security
|
||||
event_ids:
|
||||
- 4625
|
||||
- 4623
|
||||
event_level: information
|
||||
labels:
|
||||
type: eventlog
|
49
config/config_win.yaml
Normal file
49
config/config_win.yaml
Normal file
|
@ -0,0 +1,49 @@
|
|||
common:
|
||||
daemonize: true
|
||||
log_media: file
|
||||
log_level: info
|
||||
log_dir: C:\ProgramData\CrowdSec\log\
|
||||
working_dir: .
|
||||
config_paths:
|
||||
config_dir: C:\ProgramData\CrowdSec\config\
|
||||
data_dir: C:\ProgramData\CrowdSec\data\
|
||||
simulation_path: C:\ProgramData\CrowdSec\config\simulation.yaml
|
||||
hub_dir: C:\ProgramData\CrowdSec\hub\
|
||||
index_path: C:\ProgramData\CrowdSec\hub\.index.json
|
||||
plugin_dir: C:\ProgramData\CrowdSec\plugins\
|
||||
notification_dir: C:\ProgramData\CrowdSec\config\notifications\
|
||||
crowdsec_service:
|
||||
acquisition_path: C:\ProgramData\CrowdSec\config\acquis.yaml
|
||||
parser_routines: 1
|
||||
cscli:
|
||||
output: human
|
||||
db_config:
|
||||
log_level: info
|
||||
type: sqlite
|
||||
db_path: C:\ProgramData\CrowdSec\data\crowdsec.db
|
||||
#user:
|
||||
#password:
|
||||
#db_name:
|
||||
#host:
|
||||
#port:
|
||||
flush:
|
||||
max_items: 5000
|
||||
max_age: 7d
|
||||
api:
|
||||
client:
|
||||
insecure_skip_verify: false
|
||||
credentials_path: C:\ProgramData\CrowdSec\config\local_api_credentials.yaml
|
||||
server:
|
||||
log_level: info
|
||||
listen_uri: 127.0.0.1:8080
|
||||
profiles_path: C:\ProgramData\Crowdsec\config\profiles.yaml
|
||||
online_client: # Crowdsec API credentials (to push signals and receive bad IPs)
|
||||
credentials_path: C:\ProgramData\CrowdSec\config\online_api_credentials.yaml
|
||||
# tls:
|
||||
# cert_file: /etc/crowdsec/ssl/cert.pem
|
||||
# key_file: /etc/crowdsec/ssl/key.pem
|
||||
prometheus:
|
||||
enabled: true
|
||||
level: full
|
||||
listen_addr: 127.0.0.1
|
||||
listen_port: 6060
|
28
config/config_win_no_lapi.yaml
Normal file
28
config/config_win_no_lapi.yaml
Normal file
|
@ -0,0 +1,28 @@
|
|||
common:
|
||||
daemonize: true
|
||||
log_media: file
|
||||
log_level: info
|
||||
log_dir: C:\ProgramData\CrowdSec\log\
|
||||
working_dir: .
|
||||
config_paths:
|
||||
config_dir: C:\ProgramData\CrowdSec\config\
|
||||
data_dir: C:\ProgramData\CrowdSec\data\
|
||||
simulation_path: C:\ProgramData\CrowdSec\config\simulation.yaml
|
||||
hub_dir: C:\ProgramData\CrowdSec\hub\
|
||||
index_path: C:\ProgramData\CrowdSec\hub\.index.json
|
||||
plugin_dir: C:\ProgramData\CrowdSec\plugins\
|
||||
notification_dir: C:\ProgramData\CrowdSec\config\notifications\
|
||||
crowdsec_service:
|
||||
acquisition_path: C:\ProgramData\CrowdSec\config\acquis.yaml
|
||||
parser_routines: 1
|
||||
cscli:
|
||||
output: human
|
||||
api:
|
||||
client:
|
||||
insecure_skip_verify: false
|
||||
credentials_path: C:\ProgramData\CrowdSec\config\local_api_credentials.yaml
|
||||
prometheus:
|
||||
enabled: true
|
||||
level: full
|
||||
listen_addr: 127.0.0.1
|
||||
listen_port: 6060
|
12
go.mod
12
go.mod
|
@ -6,6 +6,7 @@ require (
|
|||
entgo.io/ent v0.10.1
|
||||
github.com/AlecAivazis/survey/v2 v2.2.7
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/ahmetb/dlog v0.0.0-20170105205344-4fb5f8204f26
|
||||
github.com/alexliesenfeld/health v0.5.1
|
||||
github.com/antonmedv/expr v1.8.9
|
||||
|
@ -68,10 +69,15 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
ariga.io/atlas v0.3.7 // indirect
|
||||
github.com/beevik/etree v1.1.0
|
||||
github.com/google/winops v0.0.0-20211216095627-f0e86eb1453b
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
|
||||
)
|
||||
|
||||
require (
|
||||
ariga.io/atlas v0.3.7-0.20220303204946-787354f533c3 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver v1.5.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
|
@ -100,6 +106,7 @@ require (
|
|||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
|
||||
github.com/golang/glog v0.0.0-20210429001901-424d2337a529 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/go-cmp v0.5.7 // indirect
|
||||
github.com/gorilla/mux v1.7.3 // indirect
|
||||
|
@ -153,7 +160,6 @@ require (
|
|||
go.mongodb.org/mongo-driver v1.9.0 // indirect
|
||||
golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
|
|
18
go.sum
18
go.sum
|
@ -1,5 +1,7 @@
|
|||
ariga.io/atlas v0.3.7 h1:lAISv2cc69QWzrvhlAc22Em1cbBMhJ0Xlcd8p3p3tHM=
|
||||
ariga.io/atlas v0.3.7/go.mod h1:yWGf4VPiD4SW83+kAqzD624txN9VKoJC+bpVXr2pKJA=
|
||||
ariga.io/atlas v0.3.7-0.20220303204946-787354f533c3 h1:fjG4oFCQEfGrRi0QoxWcH2OO28CE6VYa6DkIr3yDySU=
|
||||
ariga.io/atlas v0.3.7-0.20220303204946-787354f533c3/go.mod h1:yWGf4VPiD4SW83+kAqzD624txN9VKoJC+bpVXr2pKJA=
|
||||
bitbucket.org/creachadair/stringset v0.0.9 h1:L4vld9nzPt90UZNrXjNelTshD74ps4P5NGs3Iq6yN3o=
|
||||
bitbucket.org/creachadair/stringset v0.0.9/go.mod h1:t+4WcQ4+PXTa8aQdNKe40ZP6iwesoMFWAxPGd3UGjyY=
|
||||
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=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
|
@ -133,6 +135,7 @@ github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pq
|
|||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creachadair/staticfile v0.1.3/go.mod h1:a3qySzCIXEprDGxk6tSxSI+dBBdLzqeBOMhZ+o2d3pM=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
||||
|
@ -318,6 +321,7 @@ github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWe
|
|||
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
|
||||
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
|
||||
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
|
||||
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
|
@ -326,6 +330,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69
|
|||
github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v0.0.0-20210429001901-424d2337a529 h1:2voWjNECnrZRbfwXxHB1/j8wa6xdKn85B5NzgVL/pTU=
|
||||
github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
|
@ -367,11 +373,13 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/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/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
|
@ -387,12 +395,15 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
|||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/winops v0.0.0-20211216095627-f0e86eb1453b h1:THwEE9J2wPxF3BZm7WjLCASMcM7ctFzqLpTsCGh7gDY=
|
||||
github.com/google/winops v0.0.0-20211216095627-f0e86eb1453b/go.mod h1:ShbX8v8clPm/3chw9zHVwtW3QhrFpL8mXOwNxClt4pg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e h1:XmA6L9IPRdUr28a+SK/oMchGgQy159wvzXA5tJ7l+40=
|
||||
github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e/go.mod h1:AFIo+02s+12CEg8Gzz9kzhCbmbq6JcKNrhHffCGA9z4=
|
||||
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/groob/plist v0.0.0-20210519001750-9f754062e6d6/go.mod h1:itkABA+w2cw7x5nYUS/pLRef6ludkZKOigbROmCTaFw=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v1.0.0 h1:bkKf0BeBXcSYa7f5Fyi9gMuQ8gNsxeiNpZjR6VxNZeo=
|
||||
|
@ -746,7 +757,6 @@ go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qL
|
|||
go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
|
||||
go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
|
||||
go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
|
||||
go.mongodb.org/mongo-driver v1.4.4 h1:bsPHfODES+/yx2PCWzUYMH8xj6PVniPI8DQrsJuSXSs=
|
||||
go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
|
||||
go.mongodb.org/mongo-driver v1.9.0 h1:f3aLGJvQmBl8d9S40IL+jEyBC6hfLPbJjv9t5hEM9ck=
|
||||
go.mongodb.org/mongo-driver v1.9.0/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
|
||||
|
@ -935,7 +945,9 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
18
make_chocolatey.ps1
Normal file
18
make_chocolatey.ps1
Normal file
|
@ -0,0 +1,18 @@
|
|||
param (
|
||||
$version
|
||||
)
|
||||
if ($version.StartsWith("v"))
|
||||
{
|
||||
$version = $version.Substring(1)
|
||||
}
|
||||
|
||||
#Pre-releases will be like 1.4.0-rc1, remove everything after the dash as it does not conform to the MSI versioning scheme
|
||||
if ($version.Contains("-"))
|
||||
{
|
||||
$version = $version.Substring(0, $version.IndexOf("-"))
|
||||
}
|
||||
|
||||
Set-Location .\windows\Chocolatey\crowdsec
|
||||
Copy-Item ..\..\..\crowdsec_$version.msi tools\crowdsec.msi
|
||||
|
||||
choco pack
|
20
make_installer.ps1
Normal file
20
make_installer.ps1
Normal file
|
@ -0,0 +1,20 @@
|
|||
param (
|
||||
$version
|
||||
)
|
||||
$env:Path += ";C:\Program Files (x86)\WiX Toolset v3.11\bin"
|
||||
if ($version.StartsWith("v"))
|
||||
{
|
||||
$version = $version.Substring(1)
|
||||
}
|
||||
|
||||
#Pre-releases will be like 1.4.0-rc1, remove everything after the dash as it does not conform to the MSI versioning scheme
|
||||
if ($version.Contains("-"))
|
||||
{
|
||||
$version = $version.Substring(0, $version.IndexOf("-"))
|
||||
}
|
||||
|
||||
Remove-Item -Force -Recurse -Path .\msi -ErrorAction SilentlyContinue
|
||||
#we only harvest the patterns dir, as we want to handle differently some yaml files in the config directory, and I really don't want to write xlst filters to exclude the files :(
|
||||
heat.exe dir config\patterns -nologo -cg CrowdsecPatterns -dr PatternsDir -g1 -gg -sf -srd -scom -sreg -out "msi\fragment.wxs"
|
||||
candle.exe -arch x64 -dSourceDir=config\patterns -dVersion="$version" -out msi\ msi\fragment.wxs windows\installer\WixUI_HK.wxs windows\installer\product.wxs
|
||||
light.exe -b .\config\patterns -ext WixUIExtension -ext WixUtilExtension -sacl -spdb -out crowdsec_$version.msi msi\fragment.wixobj msi\WixUI_HK.wixobj msi\product.wixobj
|
|
@ -13,6 +13,7 @@ import (
|
|||
journalctlacquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/journalctl"
|
||||
kinesisacquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/kinesis"
|
||||
syslogacquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/syslog"
|
||||
wineventlogacquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/wineventlog"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -65,6 +66,10 @@ var AcquisitionSources = []struct {
|
|||
name: "kinesis",
|
||||
iface: func() DataSource { return &kinesisacquisition.KinesisSource{} },
|
||||
},
|
||||
{
|
||||
name: "wineventlog",
|
||||
iface: func() DataSource { return &wineventlogacquisition.WinEventLogSource{} },
|
||||
},
|
||||
}
|
||||
|
||||
func GetDataSourceIface(dataSourceType string) DataSource {
|
||||
|
@ -171,6 +176,7 @@ func LoadAcquisitionFromFile(config *csconfig.CrowdsecServiceCfg) ([]DataSource,
|
|||
dec.SetStrict(true)
|
||||
for {
|
||||
var sub configuration.DataSourceCommonCfg
|
||||
var idx int
|
||||
err = dec.Decode(&sub)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
|
@ -188,21 +194,23 @@ func LoadAcquisitionFromFile(config *csconfig.CrowdsecServiceCfg) ([]DataSource,
|
|||
if len(sub.Labels) == 0 {
|
||||
if sub.Source == "" {
|
||||
log.Debugf("skipping empty item in %s", acquisFile)
|
||||
idx += 1
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("missing labels in %s", acquisFile)
|
||||
return nil, fmt.Errorf("missing labels in %s (position: %d)", acquisFile, idx)
|
||||
}
|
||||
if sub.Source == "" {
|
||||
return nil, fmt.Errorf("data source type is empty ('source') in %s", acquisFile)
|
||||
return nil, fmt.Errorf("data source type is empty ('source') in %s (position: %d)", acquisFile, idx)
|
||||
}
|
||||
if GetDataSourceIface(sub.Source) == nil {
|
||||
return nil, fmt.Errorf("unknown data source %s in %s", sub.Source, acquisFile)
|
||||
return nil, fmt.Errorf("unknown data source %s in %s (position: %d)", sub.Source, acquisFile, idx)
|
||||
}
|
||||
src, err := DataSourceConfigure(sub)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "while configuring datasource of type %s from %s", sub.Source, acquisFile)
|
||||
return nil, errors.Wrapf(err, "while configuring datasource of type %s from %s (position: %d)", sub.Source, acquisFile, idx)
|
||||
}
|
||||
sources = append(sources, *src)
|
||||
idx += 1
|
||||
}
|
||||
}
|
||||
return sources, nil
|
||||
|
|
|
@ -10,6 +10,7 @@ type DataSourceCommonCfg struct {
|
|||
LogLevel *log.Level `yaml:"log_level,omitempty"`
|
||||
Source string `yaml:"source,omitempty"`
|
||||
Name string `yaml:"name,omitempty"`
|
||||
UseTimeMachine bool `yaml:"use_time_machine,omitempty"`
|
||||
Config map[string]interface{} `yaml:",inline"` //to keep the datasource-specific configuration directives
|
||||
}
|
||||
|
||||
|
|
|
@ -293,6 +293,12 @@ func (cw *CloudwatchSource) WatchLogGroupForStreams(out chan LogStreamTailConfig
|
|||
}
|
||||
cw.logger.Tracef("stream %s is elligible for monitoring", *event.LogStreamName)
|
||||
//the stream has been update recently, check if we should monitor it
|
||||
var expectMode int
|
||||
if !cw.Config.UseTimeMachine {
|
||||
expectMode = leaky.LIVE
|
||||
} else {
|
||||
expectMode = leaky.TIMEMACHINE
|
||||
}
|
||||
monitorStream := LogStreamTailConfig{
|
||||
GroupName: cw.Config.GroupName,
|
||||
StreamName: *event.LogStreamName,
|
||||
|
@ -300,7 +306,7 @@ func (cw *CloudwatchSource) WatchLogGroupForStreams(out chan LogStreamTailConfig
|
|||
PollStreamInterval: *cw.Config.PollStreamInterval,
|
||||
StreamReadTimeout: *cw.Config.StreamReadTimeout,
|
||||
PrependCloudwatchTimestamp: cw.Config.PrependCloudwatchTimestamp,
|
||||
ExpectMode: leaky.LIVE,
|
||||
ExpectMode: expectMode,
|
||||
Labels: cw.Config.Labels,
|
||||
}
|
||||
out <- monitorStream
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -37,6 +38,9 @@ func checkForLocalStackAvailability() error {
|
|||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if runtime.GOOS == "windows" {
|
||||
os.Exit(0)
|
||||
}
|
||||
if err := checkForLocalStackAvailability(); err != nil {
|
||||
log.Fatalf("local stack error : %s", err)
|
||||
}
|
||||
|
@ -49,6 +53,9 @@ func TestMain(m *testing.M) {
|
|||
}
|
||||
|
||||
func TestWatchLogGroupForStreams(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows")
|
||||
}
|
||||
var err error
|
||||
log.SetLevel(log.DebugLevel)
|
||||
tests := []struct {
|
||||
|
@ -519,6 +526,9 @@ stream_name: test_stream`),
|
|||
}
|
||||
|
||||
func TestConfiguration(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows")
|
||||
}
|
||||
var err error
|
||||
log.SetLevel(log.DebugLevel)
|
||||
tests := []struct {
|
||||
|
@ -604,6 +614,9 @@ stream_name: test_stream`),
|
|||
}
|
||||
|
||||
func TestConfigureByDSN(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows")
|
||||
}
|
||||
var err error
|
||||
log.SetLevel(log.DebugLevel)
|
||||
tests := []struct {
|
||||
|
@ -657,6 +670,9 @@ func TestConfigureByDSN(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestOneShotAcquisition(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows")
|
||||
}
|
||||
var err error
|
||||
log.SetLevel(log.DebugLevel)
|
||||
tests := []struct {
|
||||
|
|
|
@ -306,7 +306,7 @@ func (d *DockerSource) OneShotAcquisition(out chan types.Event, t *tomb.Tomb) er
|
|||
l.Process = true
|
||||
l.Module = d.GetName()
|
||||
linesRead.With(prometheus.Labels{"source": containerConfig.Name}).Inc()
|
||||
evt := types.Event{Line: l, Process: true, Type: types.LOG, ExpectMode: leaky.LIVE}
|
||||
evt := types.Event{Line: l, Process: true, Type: types.LOG, ExpectMode: leaky.TIMEMACHINE}
|
||||
out <- evt
|
||||
d.logger.Debugf("Sent line to parsing: %+v", evt.Line.Raw)
|
||||
}
|
||||
|
@ -503,7 +503,12 @@ func (d *DockerSource) TailDocker(container *ContainerConfig, outChan chan types
|
|||
l.Src = container.Name
|
||||
l.Process = true
|
||||
l.Module = d.GetName()
|
||||
evt := types.Event{Line: l, Process: true, Type: types.LOG, ExpectMode: leaky.LIVE}
|
||||
var evt types.Event
|
||||
if !d.Config.UseTimeMachine {
|
||||
evt = types.Event{Line: l, Process: true, Type: types.LOG, ExpectMode: leaky.LIVE}
|
||||
} else {
|
||||
evt = types.Event{Line: l, Process: true, Type: types.LOG, ExpectMode: leaky.TIMEMACHINE}
|
||||
}
|
||||
linesRead.With(prometheus.Labels{"source": container.Name}).Inc()
|
||||
outChan <- evt
|
||||
d.logger.Debugf("Sent line to parsing: %+v", evt.Line.Raw)
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -64,7 +65,12 @@ container_name:
|
|||
|
||||
func TestConfigureDSN(t *testing.T) {
|
||||
log.Infof("Test 'TestConfigureDSN'")
|
||||
|
||||
var dockerHost string
|
||||
if runtime.GOOS == "windows" {
|
||||
dockerHost = "npipe:////./pipe/docker_engine"
|
||||
} else {
|
||||
dockerHost = "unix:///var/run/podman/podman.sock"
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
dsn string
|
||||
|
@ -92,7 +98,7 @@ func TestConfigureDSN(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "DSN ok with multiple parameters",
|
||||
dsn: "docker://test_docker?since=42min&docker_host=unix:///var/run/podman/podman.sock",
|
||||
dsn: fmt.Sprintf("docker://test_docker?since=42min&docker_host=%s", dockerHost),
|
||||
expectedErr: "",
|
||||
},
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ func (f *FileSource) Configure(Config []byte, logger *log.Entry) error {
|
|||
f.logger.Tracef("Actual FileAcquisition Configuration %+v", f.config)
|
||||
for _, pattern := range f.config.Filenames {
|
||||
if f.config.ForceInotify {
|
||||
directory := path.Dir(pattern)
|
||||
directory := filepath.Dir(pattern)
|
||||
f.logger.Infof("Force add watch on %s", directory)
|
||||
if !f.watchedDirectories[directory] {
|
||||
err = f.watcher.Add(directory)
|
||||
|
@ -98,7 +98,8 @@ func (f *FileSource) Configure(Config []byte, logger *log.Entry) error {
|
|||
}
|
||||
for _, file := range files {
|
||||
if files[0] != pattern && f.config.Mode == configuration.TAIL_MODE { //we have a glob pattern
|
||||
directory := path.Dir(file)
|
||||
directory := filepath.Dir(file)
|
||||
f.logger.Debugf("Will add watch to directory: %s", directory)
|
||||
if !f.watchedDirectories[directory] {
|
||||
|
||||
err = f.watcher.Add(directory)
|
||||
|
@ -107,6 +108,8 @@ func (f *FileSource) Configure(Config []byte, logger *log.Entry) error {
|
|||
continue
|
||||
}
|
||||
f.watchedDirectories[directory] = true
|
||||
} else {
|
||||
f.logger.Debugf("Watch for directory %s already exists", directory)
|
||||
}
|
||||
}
|
||||
f.logger.Infof("Adding file %s to datasources", file)
|
||||
|
@ -374,7 +377,7 @@ func (f *FileSource) tailFile(out chan types.Event, t *tomb.Tomb, tail *tail.Tai
|
|||
continue
|
||||
}
|
||||
linesRead.With(prometheus.Labels{"source": tail.Filename}).Inc()
|
||||
l.Raw = line.Text
|
||||
l.Raw = trimLine(line.Text)
|
||||
l.Labels = f.config.Labels
|
||||
l.Time = line.Time
|
||||
l.Src = tail.Filename
|
||||
|
@ -382,7 +385,11 @@ func (f *FileSource) tailFile(out chan types.Event, t *tomb.Tomb, tail *tail.Tai
|
|||
l.Module = f.GetName()
|
||||
//we're tailing, it must be real time logs
|
||||
logger.Debugf("pushing %+v", l)
|
||||
if !f.config.UseTimeMachine {
|
||||
out <- types.Event{Line: l, Process: true, Type: types.LOG, ExpectMode: leaky.LIVE}
|
||||
} else {
|
||||
out <- types.Event{Line: l, Process: true, Type: types.LOG, ExpectMode: leaky.TIMEMACHINE}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package fileacquisition
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -44,6 +45,12 @@ func TestBadConfiguration(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestConfigureDSN(t *testing.T) {
|
||||
var file string
|
||||
if runtime.GOOS != "windows" {
|
||||
file = "/etc/passwd"
|
||||
} else {
|
||||
file = "C:\\Windows\\System32\\drivers\\etc\\hosts"
|
||||
}
|
||||
tests := []struct {
|
||||
dsn string
|
||||
expectedErr string
|
||||
|
@ -57,11 +64,11 @@ func TestConfigureDSN(t *testing.T) {
|
|||
expectedErr: "empty file:// DSN",
|
||||
},
|
||||
{
|
||||
dsn: "file:///etc/passwd?log_level=warn",
|
||||
dsn: fmt.Sprintf("file://%s?log_level=warn", file),
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
dsn: "file:///etc/passwd?log_level=foobar",
|
||||
dsn: fmt.Sprintf("file://%s?log_level=foobar", file),
|
||||
expectedErr: "unknown level foobar: not a valid logrus Level:",
|
||||
},
|
||||
}
|
||||
|
@ -76,6 +83,17 @@ func TestConfigureDSN(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestOneShot(t *testing.T) {
|
||||
var permDeniedFile string
|
||||
var permDeniedError string
|
||||
if runtime.GOOS != "windows" {
|
||||
permDeniedFile = "/etc/shadow"
|
||||
permDeniedError = "failed opening /etc/shadow: open /etc/shadow: permission denied"
|
||||
} else {
|
||||
//Technically, this is not a permission denied error, but we just want to test what happens
|
||||
//if we do not have access to the file
|
||||
permDeniedFile = "C:\\Windows\\System32\\config\\SAM"
|
||||
permDeniedError = "failed opening C:\\Windows\\System32\\config\\SAM: open C:\\Windows\\System32\\config\\SAM: The process cannot access the file because it is being used by another process."
|
||||
}
|
||||
tests := []struct {
|
||||
config string
|
||||
expectedConfigErr string
|
||||
|
@ -88,11 +106,11 @@ func TestOneShot(t *testing.T) {
|
|||
teardown func()
|
||||
}{
|
||||
{
|
||||
config: `
|
||||
config: fmt.Sprintf(`
|
||||
mode: cat
|
||||
filename: /etc/shadow`,
|
||||
filename: %s`, permDeniedFile),
|
||||
expectedConfigErr: "",
|
||||
expectedErr: "failed opening /etc/shadow: open /etc/shadow: permission denied",
|
||||
expectedErr: permDeniedError,
|
||||
expectedOutput: "",
|
||||
logLevel: log.WarnLevel,
|
||||
expectedLines: 0,
|
||||
|
@ -162,12 +180,13 @@ filename: test_files/bad.gz`,
|
|||
mode: cat
|
||||
filename: test_files/test_delete.log`,
|
||||
setup: func() {
|
||||
os.Create("test_files/test_delete.log")
|
||||
f, _ := os.Create("test_files/test_delete.log")
|
||||
f.Close()
|
||||
},
|
||||
afterConfigure: func() {
|
||||
os.Remove("test_files/test_delete.log")
|
||||
},
|
||||
expectedErr: "could not stat file test_files/test_delete.log : stat test_files/test_delete.log: no such file or directory",
|
||||
expectedErr: "could not stat file test_files/test_delete.log",
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -223,10 +242,25 @@ filename: test_files/test_delete.log`,
|
|||
}
|
||||
|
||||
func TestLiveAcquisition(t *testing.T) {
|
||||
var permDeniedFile string
|
||||
var permDeniedError string
|
||||
var testPattern string
|
||||
if runtime.GOOS != "windows" {
|
||||
permDeniedFile = "/etc/shadow"
|
||||
permDeniedError = "unable to read /etc/shadow : open /etc/shadow: permission denied"
|
||||
testPattern = "test_files/*.log"
|
||||
} else {
|
||||
//Technically, this is not a permission denied error, but we just want to test what happens
|
||||
//if we do not have access to the file
|
||||
permDeniedFile = "C:\\Windows\\System32\\config\\SAM"
|
||||
permDeniedError = "unable to read C:\\Windows\\System32\\config\\SAM : open C:\\Windows\\System32\\config\\SAM: The process cannot access the file because it is being used by another process"
|
||||
testPattern = "test_files\\\\*.log" // the \ must be escaped twice: once for the string, once for the yaml config
|
||||
}
|
||||
tests := []struct {
|
||||
config string
|
||||
expectedErr string
|
||||
expectedOutput string
|
||||
name string
|
||||
expectedLines int
|
||||
logLevel log.Level
|
||||
setup func()
|
||||
|
@ -234,13 +268,14 @@ func TestLiveAcquisition(t *testing.T) {
|
|||
teardown func()
|
||||
}{
|
||||
{
|
||||
config: `
|
||||
config: fmt.Sprintf(`
|
||||
mode: tail
|
||||
filename: /etc/shadow`,
|
||||
filename: %s`, permDeniedFile),
|
||||
expectedErr: "",
|
||||
expectedOutput: "unable to read /etc/shadow : open /etc/shadow: permission denied",
|
||||
expectedOutput: permDeniedError,
|
||||
logLevel: log.InfoLevel,
|
||||
expectedLines: 0,
|
||||
name: "PermissionDenied",
|
||||
},
|
||||
{
|
||||
config: `
|
||||
|
@ -250,6 +285,7 @@ filename: /`,
|
|||
expectedOutput: "/ is a directory, ignoring it",
|
||||
logLevel: log.WarnLevel,
|
||||
expectedLines: 0,
|
||||
name: "Directory",
|
||||
},
|
||||
{
|
||||
config: `
|
||||
|
@ -259,45 +295,52 @@ filename: /do/not/exist`,
|
|||
expectedOutput: "No matching files for pattern /do/not/exist",
|
||||
logLevel: log.WarnLevel,
|
||||
expectedLines: 0,
|
||||
name: "badPattern",
|
||||
},
|
||||
{
|
||||
config: `
|
||||
config: fmt.Sprintf(`
|
||||
mode: tail
|
||||
filenames:
|
||||
- test_files/*.log
|
||||
force_inotify: true`,
|
||||
- %s
|
||||
force_inotify: true`, testPattern),
|
||||
expectedErr: "",
|
||||
expectedOutput: "",
|
||||
expectedLines: 5,
|
||||
logLevel: log.DebugLevel,
|
||||
name: "basicGlob",
|
||||
},
|
||||
{
|
||||
config: `
|
||||
config: fmt.Sprintf(`
|
||||
mode: tail
|
||||
filenames:
|
||||
- test_files/*.log
|
||||
force_inotify: true`,
|
||||
- %s
|
||||
force_inotify: true`, testPattern),
|
||||
expectedErr: "",
|
||||
expectedOutput: "",
|
||||
expectedLines: 0,
|
||||
logLevel: log.DebugLevel,
|
||||
name: "GlobInotify",
|
||||
afterConfigure: func() {
|
||||
os.Create("test_files/a.log")
|
||||
f, _ := os.Create("test_files/a.log")
|
||||
f.Close()
|
||||
time.Sleep(1 * time.Second)
|
||||
os.Remove("test_files/a.log")
|
||||
},
|
||||
},
|
||||
{
|
||||
config: `
|
||||
config: fmt.Sprintf(`
|
||||
mode: tail
|
||||
filenames:
|
||||
- test_files/*.log
|
||||
force_inotify: true`,
|
||||
- %s
|
||||
force_inotify: true`, testPattern),
|
||||
expectedErr: "",
|
||||
expectedOutput: "",
|
||||
expectedLines: 5,
|
||||
logLevel: log.DebugLevel,
|
||||
name: "GlobInotifyChmod",
|
||||
afterConfigure: func() {
|
||||
os.Create("test_files/a.log")
|
||||
f, _ := os.Create("test_files/a.log")
|
||||
f.Close()
|
||||
time.Sleep(1 * time.Second)
|
||||
os.Chmod("test_files/a.log", 0000)
|
||||
},
|
||||
|
@ -307,15 +350,16 @@ force_inotify: true`,
|
|||
},
|
||||
},
|
||||
{
|
||||
config: `
|
||||
config: fmt.Sprintf(`
|
||||
mode: tail
|
||||
filenames:
|
||||
- test_files/*.log
|
||||
force_inotify: true`,
|
||||
- %s
|
||||
force_inotify: true`, testPattern),
|
||||
expectedErr: "",
|
||||
expectedOutput: "",
|
||||
expectedLines: 5,
|
||||
logLevel: log.DebugLevel,
|
||||
name: "InotifyMkDir",
|
||||
afterConfigure: func() {
|
||||
os.Mkdir("test_files/pouet/", 0700)
|
||||
},
|
||||
|
@ -326,6 +370,7 @@ force_inotify: true`,
|
|||
}
|
||||
|
||||
for _, ts := range tests {
|
||||
t.Logf("test: %s", ts.name)
|
||||
logger, hook := test.NewNullLogger()
|
||||
logger.SetLevel(ts.logLevel)
|
||||
subLogger := logger.WithFields(log.Fields{
|
||||
|
@ -377,7 +422,7 @@ force_inotify: true`,
|
|||
//we sleep to make sure we detect the new file
|
||||
time.Sleep(1 * time.Second)
|
||||
os.Remove("test_files/stream.log")
|
||||
assert.Equal(t, actualLines, ts.expectedLines)
|
||||
assert.Equal(t, ts.expectedLines, actualLines)
|
||||
}
|
||||
|
||||
if ts.expectedOutput != "" {
|
||||
|
|
7
pkg/acquisition/modules/file/tailline.go
Normal file
7
pkg/acquisition/modules/file/tailline.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
// +build linux freebsd netbsd openbsd solaris !windows
|
||||
|
||||
package fileacquisition
|
||||
|
||||
func trimLine(text string) string {
|
||||
return text
|
||||
}
|
9
pkg/acquisition/modules/file/tailline_windows.go
Normal file
9
pkg/acquisition/modules/file/tailline_windows.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
// +build windows
|
||||
|
||||
package fileacquisition
|
||||
|
||||
import "strings"
|
||||
|
||||
func trimLine(text string) string {
|
||||
return strings.TrimRight(text, "\r")
|
||||
}
|
|
@ -131,7 +131,12 @@ func (j *JournalCtlSource) runJournalCtl(out chan types.Event, t *tomb.Tomb) err
|
|||
l.Process = true
|
||||
l.Module = j.GetName()
|
||||
linesRead.With(prometheus.Labels{"source": j.src}).Inc()
|
||||
evt := types.Event{Line: l, Process: true, Type: types.LOG, ExpectMode: leaky.LIVE}
|
||||
var evt types.Event
|
||||
if !j.config.UseTimeMachine {
|
||||
evt = types.Event{Line: l, Process: true, Type: types.LOG, ExpectMode: leaky.LIVE}
|
||||
} else {
|
||||
evt = types.Event{Line: l, Process: true, Type: types.LOG, ExpectMode: leaky.TIMEMACHINE}
|
||||
}
|
||||
out <- evt
|
||||
case stderrLine := <-stderrChan:
|
||||
logger.Warnf("Got stderr message : %s", stderrLine)
|
||||
|
|
|
@ -3,6 +3,7 @@ package journalctlacquisition
|
|||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -15,6 +16,9 @@ import (
|
|||
)
|
||||
|
||||
func TestBadConfiguration(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows")
|
||||
}
|
||||
tests := []struct {
|
||||
config string
|
||||
expectedErr string
|
||||
|
@ -50,6 +54,9 @@ journalctl_filter:
|
|||
}
|
||||
|
||||
func TestConfigureDSN(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows")
|
||||
}
|
||||
tests := []struct {
|
||||
dsn string
|
||||
expectedErr string
|
||||
|
@ -94,6 +101,9 @@ func TestConfigureDSN(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestOneShot(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows")
|
||||
}
|
||||
tests := []struct {
|
||||
config string
|
||||
expectedErr string
|
||||
|
@ -182,6 +192,9 @@ journalctl_filter:
|
|||
}
|
||||
|
||||
func TestStreaming(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows")
|
||||
}
|
||||
tests := []struct {
|
||||
config string
|
||||
expectedErr string
|
||||
|
|
|
@ -296,7 +296,12 @@ func (k *KinesisSource) ParseAndPushRecords(records []*kinesis.Record, out chan
|
|||
} else {
|
||||
l.Src = k.Config.StreamName
|
||||
}
|
||||
evt := types.Event{Line: l, Process: true, Type: types.LOG, ExpectMode: leakybucket.LIVE}
|
||||
var evt types.Event
|
||||
if !k.Config.UseTimeMachine {
|
||||
evt = types.Event{Line: l, Process: true, Type: types.LOG, ExpectMode: leakybucket.LIVE}
|
||||
} else {
|
||||
evt = types.Event{Line: l, Process: true, Type: types.LOG, ExpectMode: leakybucket.TIMEMACHINE}
|
||||
}
|
||||
out <- evt
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -103,6 +104,9 @@ func TestMain(m *testing.M) {
|
|||
}
|
||||
|
||||
func TestBadConfiguration(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows")
|
||||
}
|
||||
tests := []struct {
|
||||
config string
|
||||
expectedErr string
|
||||
|
@ -144,6 +148,9 @@ stream_arn: arn:aws:kinesis:eu-west-1:123456789012:stream/my-stream`,
|
|||
}
|
||||
|
||||
func TestReadFromStream(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows")
|
||||
}
|
||||
tests := []struct {
|
||||
config string
|
||||
count int
|
||||
|
@ -187,6 +194,9 @@ stream_name: stream-1-shard`,
|
|||
}
|
||||
|
||||
func TestReadFromMultipleShards(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows")
|
||||
}
|
||||
tests := []struct {
|
||||
config string
|
||||
count int
|
||||
|
@ -232,6 +242,9 @@ stream_name: stream-2-shards`,
|
|||
}
|
||||
|
||||
func TestFromSubscription(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows")
|
||||
}
|
||||
tests := []struct {
|
||||
config string
|
||||
count int
|
||||
|
|
|
@ -238,7 +238,11 @@ func (s *SyslogSource) handleSyslogMsg(out chan types.Event, t *tomb.Tomb, c cha
|
|||
l.Time = ts
|
||||
l.Src = syslogLine.Client
|
||||
l.Process = true
|
||||
if !s.config.UseTimeMachine {
|
||||
out <- types.Event{Line: l, Process: true, Type: types.LOG, ExpectMode: leaky.LIVE}
|
||||
} else {
|
||||
out <- types.Event{Line: l, Process: true, Type: types.LOG, ExpectMode: leaky.TIMEMACHINE}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package syslogacquisition
|
|||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -77,10 +78,6 @@ func TestStreamingAcquisition(t *testing.T) {
|
|||
logs []string
|
||||
expectedLines int
|
||||
}{
|
||||
{
|
||||
config: `source: syslog`,
|
||||
expectedErr: "could not start syslog server: could not listen on port 514: listen udp 127.0.0.1:514: bind: permission denied",
|
||||
},
|
||||
{
|
||||
config: `
|
||||
source: syslog
|
||||
|
@ -109,6 +106,17 @@ listen_addr: 127.0.0.1`,
|
|||
`<13>May 18 12:37:56 mantis sshd`},
|
||||
},
|
||||
}
|
||||
if runtime.GOOS != "windows" {
|
||||
tests = append(tests, struct {
|
||||
config string
|
||||
expectedErr string
|
||||
logs []string
|
||||
expectedLines int
|
||||
}{
|
||||
config: `source: syslog`,
|
||||
expectedErr: "could not start syslog server: could not listen on port 514: listen udp 127.0.0.1:514: bind: permission denied",
|
||||
})
|
||||
}
|
||||
|
||||
for _, ts := range tests {
|
||||
subLogger := log.WithFields(log.Fields{
|
||||
|
|
59
pkg/acquisition/modules/wineventlog/wineventlog.go
Normal file
59
pkg/acquisition/modules/wineventlog/wineventlog.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
//go:build !windows
|
||||
|
||||
package wineventlogacquisition
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/tomb.v2"
|
||||
)
|
||||
|
||||
type WinEventLogSource struct{}
|
||||
|
||||
func (w *WinEventLogSource) Configure(yamlConfig []byte, logger *log.Entry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) ConfigureByDSN(dsn string, labels map[string]string, logger *log.Entry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) GetMode() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) SupportedModes() []string {
|
||||
return []string{configuration.TAIL_MODE, configuration.CAT_MODE}
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) OneShotAcquisition(out chan types.Event, t *tomb.Tomb) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) GetMetrics() []prometheus.Collector {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) GetAggregMetrics() []prometheus.Collector {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) GetName() string {
|
||||
return "wineventlog"
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) CanRun() error {
|
||||
return errors.New("windows event log acquisition is only supported on Windows")
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) StreamingAcquisition(out chan types.Event, t *tomb.Tomb) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) Dump() interface{} {
|
||||
return w
|
||||
}
|
233
pkg/acquisition/modules/wineventlog/wineventlog_test.go
Normal file
233
pkg/acquisition/modules/wineventlog/wineventlog_test.go
Normal file
|
@ -0,0 +1,233 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package wineventlogacquisition
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/sys/windows/svc/eventlog"
|
||||
"gopkg.in/tomb.v2"
|
||||
)
|
||||
|
||||
func TestBadConfiguration(t *testing.T) {
|
||||
if runtime.GOOS != "windows" {
|
||||
t.Skip("Skipping test on non-windows OS")
|
||||
}
|
||||
tests := []struct {
|
||||
config string
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
config: `source: wineventlog
|
||||
foobar: 42`,
|
||||
expectedErr: "field foobar not found in type wineventlogacquisition.WinEventLogConfiguration",
|
||||
},
|
||||
{
|
||||
config: `source: wineventlog`,
|
||||
expectedErr: "event_channel or xpath_query must be set",
|
||||
},
|
||||
{
|
||||
config: `source: wineventlog
|
||||
event_channel: Security
|
||||
event_level: blabla`,
|
||||
expectedErr: "buildXpathQuery failed: invalid log level",
|
||||
},
|
||||
{
|
||||
config: `source: wineventlog
|
||||
event_channel: Security
|
||||
event_level: blabla`,
|
||||
expectedErr: "buildXpathQuery failed: invalid log level",
|
||||
},
|
||||
{
|
||||
config: `source: wineventlog
|
||||
event_channel: foo
|
||||
xpath_query: test`,
|
||||
expectedErr: "event_channel and xpath_query are mutually exclusive",
|
||||
},
|
||||
}
|
||||
|
||||
subLogger := log.WithFields(log.Fields{
|
||||
"type": "windowseventlog",
|
||||
})
|
||||
for _, test := range tests {
|
||||
f := WinEventLogSource{}
|
||||
err := f.Configure([]byte(test.config), subLogger)
|
||||
assert.Contains(t, err.Error(), test.expectedErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryBuilder(t *testing.T) {
|
||||
if runtime.GOOS != "windows" {
|
||||
t.Skip("Skipping test on non-windows OS")
|
||||
}
|
||||
tests := []struct {
|
||||
config string
|
||||
expectedQuery string
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
config: `source: wineventlog
|
||||
event_channel: Security
|
||||
event_level: Information`,
|
||||
expectedQuery: "<QueryList><Query><Select Path=\"Security\">*[System[(Level=0 or Level=4)]]</Select></Query></QueryList>",
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
config: `source: wineventlog
|
||||
event_channel: Security
|
||||
event_level: Error
|
||||
event_ids:
|
||||
- 42`,
|
||||
expectedQuery: "<QueryList><Query><Select Path=\"Security\">*[System[(EventID=42) and (Level=2)]]</Select></Query></QueryList>",
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
config: `source: wineventlog
|
||||
event_channel: Security
|
||||
event_level: Error
|
||||
event_ids:
|
||||
- 42
|
||||
- 43`,
|
||||
expectedQuery: "<QueryList><Query><Select Path=\"Security\">*[System[(EventID=42 or EventID=43) and (Level=2)]]</Select></Query></QueryList>",
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
config: `source: wineventlog
|
||||
event_channel: Security`,
|
||||
expectedQuery: "<QueryList><Query><Select Path=\"Security\">*</Select></Query></QueryList>",
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
config: `source: wineventlog
|
||||
event_channel: Security
|
||||
event_level: bla`,
|
||||
expectedQuery: "",
|
||||
expectedErr: "invalid log level",
|
||||
},
|
||||
}
|
||||
subLogger := log.WithFields(log.Fields{
|
||||
"type": "windowseventlog",
|
||||
})
|
||||
for _, test := range tests {
|
||||
f := WinEventLogSource{}
|
||||
f.Configure([]byte(test.config), subLogger)
|
||||
q, err := f.buildXpathQuery()
|
||||
if test.expectedErr != "" {
|
||||
if err == nil {
|
||||
t.Fatalf("expected error '%s' but got none", test.expectedErr)
|
||||
}
|
||||
assert.Contains(t, err.Error(), test.expectedErr)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, test.expectedQuery, q)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLiveAcquisition(t *testing.T) {
|
||||
if runtime.GOOS != "windows" {
|
||||
t.Skip("Skipping test on non-windows OS")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
config string
|
||||
expectedLines []string
|
||||
}{
|
||||
{
|
||||
config: `source: wineventlog
|
||||
xpath_query: |
|
||||
<QueryList>
|
||||
<Query Id="0" Path="Application">
|
||||
<Select Path="Application">*[System[(Level=4 or Level=0) and (EventID=42)]]</Select>
|
||||
</Query>
|
||||
</QueryList>`,
|
||||
expectedLines: []string{
|
||||
"blabla",
|
||||
"test",
|
||||
"aaaa",
|
||||
"bbbbb",
|
||||
},
|
||||
},
|
||||
{
|
||||
config: `source: wineventlog
|
||||
xpath_query: |
|
||||
<sdf>asdfsdf`,
|
||||
expectedLines: nil,
|
||||
},
|
||||
{
|
||||
config: `source: wineventlog
|
||||
event_channel: Application
|
||||
event_level: Information
|
||||
event_ids:
|
||||
- 42`,
|
||||
expectedLines: []string{
|
||||
"testmessage",
|
||||
},
|
||||
},
|
||||
{
|
||||
config: `source: wineventlog
|
||||
event_channel: Application
|
||||
event_level: Information
|
||||
event_ids:
|
||||
- 43`,
|
||||
expectedLines: nil,
|
||||
},
|
||||
}
|
||||
subLogger := log.WithFields(log.Fields{
|
||||
"type": "windowseventlog",
|
||||
})
|
||||
|
||||
evthandler, err := eventlog.Open("Application")
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open event log: %s", err)
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
to := &tomb.Tomb{}
|
||||
c := make(chan types.Event)
|
||||
f := WinEventLogSource{}
|
||||
f.Configure([]byte(test.config), subLogger)
|
||||
f.StreamingAcquisition(c, to)
|
||||
time.Sleep(time.Second)
|
||||
lines := test.expectedLines
|
||||
go func() {
|
||||
for _, line := range lines {
|
||||
evthandler.Info(42, line)
|
||||
}
|
||||
}()
|
||||
ticker := time.NewTicker(time.Second * 5)
|
||||
linesRead := make([]string, 0)
|
||||
READLOOP:
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if test.expectedLines == nil {
|
||||
break READLOOP
|
||||
}
|
||||
t.Fatalf("timeout")
|
||||
case e := <-c:
|
||||
|
||||
linesRead = append(linesRead, exprhelpers.XMLGetNodeValue(e.Line.Raw, "/Event/EventData[1]/Data"))
|
||||
if len(linesRead) == len(lines) {
|
||||
break READLOOP
|
||||
}
|
||||
}
|
||||
}
|
||||
if test.expectedLines == nil {
|
||||
assert.Equal(t, 0, len(linesRead))
|
||||
} else {
|
||||
assert.Equal(t, len(test.expectedLines), len(linesRead))
|
||||
assert.Equal(t, test.expectedLines, linesRead)
|
||||
}
|
||||
to.Kill(nil)
|
||||
to.Wait()
|
||||
}
|
||||
}
|
320
pkg/acquisition/modules/wineventlog/wineventlog_windows.go
Normal file
320
pkg/acquisition/modules/wineventlog/wineventlog_windows.go
Normal file
|
@ -0,0 +1,320 @@
|
|||
package wineventlogacquisition
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
|
||||
leaky "github.com/crowdsecurity/crowdsec/pkg/leakybucket"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
"github.com/google/winops/winlog"
|
||||
"github.com/google/winops/winlog/wevtapi"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/windows"
|
||||
"gopkg.in/tomb.v2"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type WinEventLogConfiguration struct {
|
||||
configuration.DataSourceCommonCfg `yaml:",inline"`
|
||||
EventChannel string `yaml:"event_channel"`
|
||||
EventLevel string `yaml:"event_level"`
|
||||
EventIDs []int `yaml:"event_ids"`
|
||||
XPathQuery string `yaml:"xpath_query"`
|
||||
EventFile string `yaml:"event_file"`
|
||||
PrettyName string `yaml:"pretty_name"`
|
||||
}
|
||||
|
||||
type WinEventLogSource struct {
|
||||
config WinEventLogConfiguration
|
||||
logger *log.Entry
|
||||
evtConfig *winlog.SubscribeConfig
|
||||
query string
|
||||
name string
|
||||
}
|
||||
|
||||
type QueryList struct {
|
||||
Select Select `xml:"Query>Select"`
|
||||
}
|
||||
|
||||
type Select struct {
|
||||
Path string `xml:"Path,attr"`
|
||||
Query string `xml:",chardata"`
|
||||
}
|
||||
|
||||
var linesRead = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "cs_winevtlogsource_hits_total",
|
||||
Help: "Total event that were read.",
|
||||
},
|
||||
[]string{"source"})
|
||||
|
||||
func logLevelToInt(logLevel string) ([]string, error) {
|
||||
switch strings.ToUpper(logLevel) {
|
||||
case "CRITICAL":
|
||||
return []string{"1"}, nil
|
||||
case "ERROR":
|
||||
return []string{"2"}, nil
|
||||
case "WARNING":
|
||||
return []string{"3"}, nil
|
||||
case "INFORMATION":
|
||||
return []string{"0", "4"}, nil
|
||||
case "VERBOSE":
|
||||
return []string{"5"}, nil
|
||||
default:
|
||||
return nil, errors.New("invalid log level")
|
||||
}
|
||||
}
|
||||
|
||||
//This is lifted from winops/winlog, but we only want to render the basic XML string, we don't need the extra fluff
|
||||
func (w *WinEventLogSource) getXMLEvents(config *winlog.SubscribeConfig, publisherCache map[string]windows.Handle, resultSet windows.Handle, maxEvents int) ([]string, error) {
|
||||
var events = make([]windows.Handle, maxEvents)
|
||||
var returned uint32
|
||||
|
||||
// Get handles to events from the result set.
|
||||
err := wevtapi.EvtNext(
|
||||
resultSet, // Handle to query or subscription result set.
|
||||
uint32(len(events)), // The number of events to attempt to retrieve.
|
||||
&events[0], // Pointer to the array of event handles.
|
||||
2000, // Timeout in milliseconds to wait.
|
||||
0, // Reserved. Must be zero.
|
||||
&returned) // The number of handles in the array that are set by the API.
|
||||
if err == windows.ERROR_NO_MORE_ITEMS {
|
||||
return nil, err
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("wevtapi.EvtNext failed: %v", err)
|
||||
}
|
||||
|
||||
// Event handles must be closed after they are returned by EvtNext whether or not we use them.
|
||||
defer func() {
|
||||
for _, event := range events[:returned] {
|
||||
winlog.Close(event)
|
||||
}
|
||||
}()
|
||||
|
||||
// Render events.
|
||||
var renderedEvents []string
|
||||
for _, event := range events[:returned] {
|
||||
// Render the basic XML representation of the event.
|
||||
fragment, err := winlog.RenderFragment(event, wevtapi.EvtRenderEventXml)
|
||||
if err != nil {
|
||||
w.logger.Errorf("Failed to render event with RenderFragment, skipping: %v", err)
|
||||
continue
|
||||
}
|
||||
w.logger.Tracef("Rendered event: %s", fragment)
|
||||
renderedEvents = append(renderedEvents, fragment)
|
||||
}
|
||||
return renderedEvents, err
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) buildXpathQuery() (string, error) {
|
||||
var query string
|
||||
queryComponents := [][]string{}
|
||||
if w.config.EventIDs != nil {
|
||||
eventIds := []string{}
|
||||
for _, id := range w.config.EventIDs {
|
||||
eventIds = append(eventIds, fmt.Sprintf("EventID=%d", id))
|
||||
}
|
||||
queryComponents = append(queryComponents, eventIds)
|
||||
}
|
||||
if w.config.EventLevel != "" {
|
||||
levels, err := logLevelToInt(w.config.EventLevel)
|
||||
logLevels := []string{}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, level := range levels {
|
||||
logLevels = append(logLevels, fmt.Sprintf("Level=%s", level))
|
||||
}
|
||||
queryComponents = append(queryComponents, logLevels)
|
||||
}
|
||||
if len(queryComponents) > 0 {
|
||||
andList := []string{}
|
||||
for _, component := range queryComponents {
|
||||
andList = append(andList, fmt.Sprintf("(%s)", strings.Join(component, " or ")))
|
||||
}
|
||||
query = fmt.Sprintf("*[System[%s]]", strings.Join(andList, " and "))
|
||||
} else {
|
||||
query = "*"
|
||||
}
|
||||
queryList := QueryList{Select: Select{Path: w.config.EventChannel, Query: query}}
|
||||
xpathQuery, err := xml.Marshal(queryList)
|
||||
if err != nil {
|
||||
w.logger.Errorf("Marshal failed: %v", err)
|
||||
return "", err
|
||||
}
|
||||
w.logger.Debugf("xpathQuery: %s", xpathQuery)
|
||||
return string(xpathQuery), nil
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) getEvents(out chan types.Event, t *tomb.Tomb) error {
|
||||
subscription, err := winlog.Subscribe(w.evtConfig)
|
||||
if err != nil {
|
||||
w.logger.Errorf("Failed to subscribe to event log: %s", err)
|
||||
return err
|
||||
}
|
||||
defer winlog.Close(subscription)
|
||||
publisherCache := make(map[string]windows.Handle)
|
||||
defer func() {
|
||||
for _, h := range publisherCache {
|
||||
winlog.Close(h)
|
||||
}
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-t.Dying():
|
||||
w.logger.Infof("wineventlog is dying")
|
||||
return nil
|
||||
default:
|
||||
status, err := windows.WaitForSingleObject(w.evtConfig.SignalEvent, 1000)
|
||||
if err != nil {
|
||||
w.logger.Errorf("WaitForSingleObject failed: %s", err)
|
||||
return err
|
||||
}
|
||||
if status == syscall.WAIT_OBJECT_0 {
|
||||
renderedEvents, err := w.getXMLEvents(w.evtConfig, publisherCache, subscription, 500)
|
||||
if err == windows.ERROR_NO_MORE_ITEMS {
|
||||
windows.ResetEvent(w.evtConfig.SignalEvent)
|
||||
} else if err != nil {
|
||||
w.logger.Errorf("getXMLEvents failed: %v", err)
|
||||
continue
|
||||
}
|
||||
for _, event := range renderedEvents {
|
||||
linesRead.With(prometheus.Labels{"source": w.name}).Inc()
|
||||
l := types.Line{}
|
||||
l.Raw = event
|
||||
l.Module = w.GetName()
|
||||
l.Labels = w.config.Labels
|
||||
l.Time = time.Now()
|
||||
l.Src = w.name
|
||||
l.Process = true
|
||||
if !w.config.UseTimeMachine {
|
||||
out <- types.Event{Line: l, Process: true, Type: types.LOG, ExpectMode: leaky.LIVE}
|
||||
} else {
|
||||
out <- types.Event{Line: l, Process: true, Type: types.LOG, ExpectMode: leaky.TIMEMACHINE}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) generateConfig(query string) (*winlog.SubscribeConfig, error) {
|
||||
var config winlog.SubscribeConfig
|
||||
var err error
|
||||
|
||||
// Create a subscription signaler.
|
||||
config.SignalEvent, err = windows.CreateEvent(
|
||||
nil, // Default security descriptor.
|
||||
1, // Manual reset.
|
||||
1, // Initial state is signaled.
|
||||
nil) // Optional name.
|
||||
if err != nil {
|
||||
return &config, fmt.Errorf("windows.CreateEvent failed: %v", err)
|
||||
}
|
||||
config.Flags = wevtapi.EvtSubscribeToFutureEvents
|
||||
config.Query, err = syscall.UTF16PtrFromString(query)
|
||||
if err != nil {
|
||||
return &config, fmt.Errorf("syscall.UTF16PtrFromString failed: %v", err)
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) Configure(yamlConfig []byte, logger *log.Entry) error {
|
||||
|
||||
config := WinEventLogConfiguration{}
|
||||
w.logger = logger
|
||||
err := yaml.UnmarshalStrict(yamlConfig, &config)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse configuration: %v", err)
|
||||
}
|
||||
|
||||
if config.EventChannel != "" && config.XPathQuery != "" {
|
||||
return fmt.Errorf("event_channel and xpath_query are mutually exclusive")
|
||||
}
|
||||
|
||||
if config.EventChannel == "" && config.XPathQuery == "" {
|
||||
return fmt.Errorf("event_channel or xpath_query must be set")
|
||||
}
|
||||
|
||||
config.Mode = configuration.TAIL_MODE
|
||||
w.config = config
|
||||
|
||||
if config.XPathQuery != "" {
|
||||
w.query = config.XPathQuery
|
||||
} else {
|
||||
w.query, err = w.buildXpathQuery()
|
||||
if err != nil {
|
||||
return fmt.Errorf("buildXpathQuery failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
w.evtConfig, err = w.generateConfig(w.query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.PrettyName != "" {
|
||||
w.name = config.PrettyName
|
||||
} else {
|
||||
w.name = w.query
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) ConfigureByDSN(dsn string, labels map[string]string, logger *log.Entry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) GetMode() string {
|
||||
return w.config.Mode
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) SupportedModes() []string {
|
||||
return []string{configuration.TAIL_MODE}
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) OneShotAcquisition(out chan types.Event, t *tomb.Tomb) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) GetMetrics() []prometheus.Collector {
|
||||
return []prometheus.Collector{linesRead}
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) GetAggregMetrics() []prometheus.Collector {
|
||||
return []prometheus.Collector{linesRead}
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) GetName() string {
|
||||
return "wineventlog"
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) CanRun() error {
|
||||
if runtime.GOOS != "windows" {
|
||||
return errors.New("windows event log acquisition is only supported on Windows")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) StreamingAcquisition(out chan types.Event, t *tomb.Tomb) error {
|
||||
t.Go(func() error {
|
||||
defer types.CatchPanic("crowdsec/acquis/wineventlog/streaming")
|
||||
return w.getEvents(out, t)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WinEventLogSource) Dump() interface{} {
|
||||
return w
|
||||
}
|
|
@ -6,6 +6,7 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -142,7 +143,11 @@ func TestNewClientRegisterKO(t *testing.T) {
|
|||
URL: apiURL,
|
||||
VersionPrefix: "v1",
|
||||
}, &http.Client{})
|
||||
if runtime.GOOS != "windows" {
|
||||
assert.Contains(t, fmt.Sprintf("%s", err), "dial tcp 127.0.0.1:4242: connect: connection refused")
|
||||
} else {
|
||||
assert.Contains(t, fmt.Sprintf("%s", err), " No connection could be made because the target machine actively refused it.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewClientRegisterOK(t *testing.T) {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csplugin"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -26,12 +27,12 @@ type LAPI struct {
|
|||
|
||||
func SetupLAPITest(t *testing.T) LAPI {
|
||||
t.Helper()
|
||||
router, loginResp, err := InitMachineTest()
|
||||
router, loginResp, config, err := InitMachineTest()
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
APIKey, err := CreateTestBouncer()
|
||||
APIKey, err := CreateTestBouncer(config.API.Server.DbConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err.Error())
|
||||
}
|
||||
|
@ -59,25 +60,25 @@ func (l *LAPI) RecordResponse(verb string, url string, body *strings.Reader) *ht
|
|||
return w
|
||||
}
|
||||
|
||||
func InitMachineTest() (*gin.Engine, models.WatcherAuthResponse, error) {
|
||||
router, err := NewAPITest()
|
||||
func InitMachineTest() (*gin.Engine, models.WatcherAuthResponse, csconfig.Config, error) {
|
||||
router, config, err := NewAPITest()
|
||||
if err != nil {
|
||||
return nil, models.WatcherAuthResponse{}, fmt.Errorf("unable to run local API: %s", err)
|
||||
return nil, models.WatcherAuthResponse{}, config, fmt.Errorf("unable to run local API: %s", err)
|
||||
}
|
||||
|
||||
loginResp, err := LoginToTestAPI(router)
|
||||
loginResp, err := LoginToTestAPI(router, config)
|
||||
if err != nil {
|
||||
return nil, models.WatcherAuthResponse{}, fmt.Errorf("%s", err.Error())
|
||||
return nil, models.WatcherAuthResponse{}, config, fmt.Errorf("%s", err.Error())
|
||||
}
|
||||
return router, loginResp, nil
|
||||
return router, loginResp, config, nil
|
||||
}
|
||||
|
||||
func LoginToTestAPI(router *gin.Engine) (models.WatcherAuthResponse, error) {
|
||||
func LoginToTestAPI(router *gin.Engine, config csconfig.Config) (models.WatcherAuthResponse, error) {
|
||||
body, err := CreateTestMachine(router)
|
||||
if err != nil {
|
||||
return models.WatcherAuthResponse{}, fmt.Errorf("%s", err.Error())
|
||||
}
|
||||
err = ValidateMachine("test")
|
||||
err = ValidateMachine("test", config.API.Server.DbConfig)
|
||||
if err != nil {
|
||||
log.Fatalln(err.Error())
|
||||
}
|
||||
|
@ -141,14 +142,14 @@ func TestCreateAlert(t *testing.T) {
|
|||
|
||||
func TestCreateAlertChannels(t *testing.T) {
|
||||
|
||||
apiServer, err := NewAPIServer()
|
||||
apiServer, config, err := NewAPIServer()
|
||||
if err != nil {
|
||||
log.Fatalln(err.Error())
|
||||
}
|
||||
apiServer.controller.PluginChannel = make(chan csplugin.ProfileAlert)
|
||||
apiServer.InitController()
|
||||
|
||||
loginResp, err := LoginToTestAPI(apiServer.router)
|
||||
loginResp, err := LoginToTestAPI(apiServer.router, config)
|
||||
if err != nil {
|
||||
log.Fatalln(err.Error())
|
||||
}
|
||||
|
@ -427,7 +428,7 @@ func TestDeleteAlertTrustedIPS(t *testing.T) {
|
|||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
loginResp, err := LoginToTestAPI(router)
|
||||
loginResp, err := LoginToTestAPI(router, cfg)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
|
|
@ -11,12 +11,12 @@ import (
|
|||
)
|
||||
|
||||
func TestAPIKey(t *testing.T) {
|
||||
router, err := NewAPITest()
|
||||
router, config, err := NewAPITest()
|
||||
if err != nil {
|
||||
log.Fatalf("unable to run local API: %s", err)
|
||||
}
|
||||
|
||||
APIKey, err := CreateTestBouncer()
|
||||
APIKey, err := CreateTestBouncer(config.API.Server.DbConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("%s", err.Error())
|
||||
}
|
||||
|
|
|
@ -728,15 +728,15 @@ func TestAPICSendMetrics(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
name: "basic",
|
||||
duration: time.Millisecond * 5,
|
||||
metricsInterval: time.Millisecond,
|
||||
duration: time.Millisecond * 30,
|
||||
metricsInterval: time.Millisecond * 5,
|
||||
expectedCalls: 5,
|
||||
setUp: func() {},
|
||||
},
|
||||
{
|
||||
name: "with some metrics",
|
||||
duration: time.Millisecond * 5,
|
||||
metricsInterval: time.Millisecond,
|
||||
duration: time.Millisecond * 30,
|
||||
metricsInterval: time.Millisecond * 5,
|
||||
expectedCalls: 5,
|
||||
setUp: func() {
|
||||
api.dbClient.Ent.Machine.Create().
|
||||
|
@ -862,7 +862,8 @@ func TestAPICPull(t *testing.T) {
|
|||
panic(err)
|
||||
}
|
||||
}()
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
//Slightly long because the CI runner for windows are slow, and this can lead to random failure
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
logrus.SetOutput(os.Stderr)
|
||||
assert.Contains(t, buf.String(), testCase.logContains)
|
||||
assertTotalDecisionCount(t, api.dbClient, testCase.expectedDecisionCount)
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -41,9 +42,10 @@ func LoadTestConfig() csconfig.Config {
|
|||
flushConfig := csconfig.FlushDBCfg{
|
||||
MaxAge: &maxAge,
|
||||
}
|
||||
tempDir, _ := os.MkdirTemp("", "crowdsec_tests")
|
||||
dbconfig := csconfig.DatabaseCfg{
|
||||
Type: "sqlite",
|
||||
DbPath: "./ent",
|
||||
DbPath: filepath.Join(tempDir, "ent"),
|
||||
Flush: &flushConfig,
|
||||
}
|
||||
apiServerConfig := csconfig.LocalApiServerCfg{
|
||||
|
@ -72,9 +74,10 @@ func LoadTestConfigForwardedFor() csconfig.Config {
|
|||
flushConfig := csconfig.FlushDBCfg{
|
||||
MaxAge: &maxAge,
|
||||
}
|
||||
tempDir, _ := os.MkdirTemp("", "crowdsec_tests")
|
||||
dbconfig := csconfig.DatabaseCfg{
|
||||
Type: "sqlite",
|
||||
DbPath: "./ent",
|
||||
DbPath: filepath.Join(tempDir, "ent"),
|
||||
Flush: &flushConfig,
|
||||
}
|
||||
apiServerConfig := csconfig.LocalApiServerCfg{
|
||||
|
@ -99,58 +102,57 @@ func LoadTestConfigForwardedFor() csconfig.Config {
|
|||
return config
|
||||
}
|
||||
|
||||
func NewAPIServer() (*APIServer, error) {
|
||||
func NewAPIServer() (*APIServer, csconfig.Config, error) {
|
||||
config := LoadTestConfig()
|
||||
os.Remove("./ent")
|
||||
apiServer, err := NewServer(config.API.Server)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to run local API: %s", err)
|
||||
return nil, config, fmt.Errorf("unable to run local API: %s", err)
|
||||
}
|
||||
log.Printf("Creating new API server")
|
||||
gin.SetMode(gin.TestMode)
|
||||
return apiServer, nil
|
||||
return apiServer, config, nil
|
||||
}
|
||||
|
||||
func NewAPITest() (*gin.Engine, error) {
|
||||
apiServer, err := NewAPIServer()
|
||||
func NewAPITest() (*gin.Engine, csconfig.Config, error) {
|
||||
apiServer, config, err := NewAPIServer()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to run local API: %s", err)
|
||||
return nil, config, 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)
|
||||
return nil, config, 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)
|
||||
return nil, config, fmt.Errorf("unable to run local API: %s", err)
|
||||
}
|
||||
return router, nil
|
||||
return router, config, nil
|
||||
}
|
||||
|
||||
func NewAPITestForwardedFor() (*gin.Engine, error) {
|
||||
func NewAPITestForwardedFor() (*gin.Engine, csconfig.Config, error) {
|
||||
config := LoadTestConfigForwardedFor()
|
||||
|
||||
os.Remove("./ent")
|
||||
apiServer, err := NewServer(config.API.Server)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to run local API: %s", err)
|
||||
return nil, config, 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)
|
||||
return nil, config, fmt.Errorf("unable to run local API: %s", err)
|
||||
}
|
||||
log.Printf("Creating new API server")
|
||||
gin.SetMode(gin.TestMode)
|
||||
router, err := apiServer.Router()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to run local API: %s", err)
|
||||
return nil, config, fmt.Errorf("unable to run local API: %s", err)
|
||||
}
|
||||
return router, nil
|
||||
return router, config, nil
|
||||
}
|
||||
|
||||
func ValidateMachine(machineID string) error {
|
||||
config := LoadTestConfig()
|
||||
dbClient, err := database.NewClient(config.API.Server.DbConfig)
|
||||
func ValidateMachine(machineID string, config *csconfig.DatabaseCfg) error {
|
||||
dbClient, err := database.NewClient(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create new database client: %s", err)
|
||||
}
|
||||
|
@ -160,9 +162,8 @@ func ValidateMachine(machineID string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func GetMachineIP(machineID string) (string, error) {
|
||||
config := LoadTestConfig()
|
||||
dbClient, err := database.NewClient(config.API.Server.DbConfig)
|
||||
func GetMachineIP(machineID string, config *csconfig.DatabaseCfg) (string, error) {
|
||||
dbClient, err := database.NewClient(config)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to create new database client: %s", err)
|
||||
}
|
||||
|
@ -265,10 +266,8 @@ func CreateTestMachine(router *gin.Engine) (string, error) {
|
|||
return body, nil
|
||||
}
|
||||
|
||||
func CreateTestBouncer() (string, error) {
|
||||
config := LoadTestConfig()
|
||||
|
||||
dbClient, err := database.NewClient(config.API.Server.DbConfig)
|
||||
func CreateTestBouncer(config *csconfig.DatabaseCfg) (string, error) {
|
||||
dbClient, err := database.NewClient(config)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to create new database client: %s", err)
|
||||
}
|
||||
|
@ -304,7 +303,7 @@ func TestWithWrongFlushConfig(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestUnknownPath(t *testing.T) {
|
||||
router, err := NewAPITest()
|
||||
router, _, err := NewAPITest()
|
||||
if err != nil {
|
||||
log.Fatalf("unable to run local API: %s", err)
|
||||
}
|
||||
|
@ -340,25 +339,23 @@ func TestLoggingDebugToFileConfig(t *testing.T) {
|
|||
flushConfig := csconfig.FlushDBCfg{
|
||||
MaxAge: &maxAge,
|
||||
}
|
||||
tempDir, _ := os.MkdirTemp("", "crowdsec_tests")
|
||||
dbconfig := csconfig.DatabaseCfg{
|
||||
Type: "sqlite",
|
||||
DbPath: "./ent",
|
||||
DbPath: filepath.Join(tempDir, "ent"),
|
||||
Flush: &flushConfig,
|
||||
}
|
||||
cfg := csconfig.LocalApiServerCfg{
|
||||
ListenURI: "127.0.0.1:8080",
|
||||
LogMedia: "file",
|
||||
LogDir: ".",
|
||||
LogDir: tempDir,
|
||||
DbConfig: &dbconfig,
|
||||
}
|
||||
lvl := log.DebugLevel
|
||||
expectedFile := "./crowdsec_api.log"
|
||||
expectedFile := fmt.Sprintf("%s/crowdsec_api.log", tempDir)
|
||||
expectedLines := []string{"/test42"}
|
||||
cfg.LogLevel = &lvl
|
||||
|
||||
os.Remove("./crowdsec.log")
|
||||
os.Remove(expectedFile)
|
||||
|
||||
// Configure logging
|
||||
if err := types.SetDefaultLoggerConfig(cfg.LogMedia, cfg.LogDir, *cfg.LogLevel, cfg.LogMaxSize, cfg.LogMaxFiles, cfg.LogMaxAge, cfg.CompressLogs); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
|
@ -391,9 +388,6 @@ func TestLoggingDebugToFileConfig(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
os.Remove("./crowdsec.log")
|
||||
os.Remove(expectedFile)
|
||||
|
||||
}
|
||||
|
||||
func TestLoggingErrorToFileConfig(t *testing.T) {
|
||||
|
@ -403,24 +397,22 @@ func TestLoggingErrorToFileConfig(t *testing.T) {
|
|||
flushConfig := csconfig.FlushDBCfg{
|
||||
MaxAge: &maxAge,
|
||||
}
|
||||
tempDir, _ := os.MkdirTemp("", "crowdsec_tests")
|
||||
dbconfig := csconfig.DatabaseCfg{
|
||||
Type: "sqlite",
|
||||
DbPath: "./ent",
|
||||
DbPath: filepath.Join(tempDir, "ent"),
|
||||
Flush: &flushConfig,
|
||||
}
|
||||
cfg := csconfig.LocalApiServerCfg{
|
||||
ListenURI: "127.0.0.1:8080",
|
||||
LogMedia: "file",
|
||||
LogDir: ".",
|
||||
LogDir: tempDir,
|
||||
DbConfig: &dbconfig,
|
||||
}
|
||||
lvl := log.ErrorLevel
|
||||
expectedFile := "./crowdsec_api.log"
|
||||
expectedFile := fmt.Sprintf("%s/crowdsec_api.log", tempDir)
|
||||
cfg.LogLevel = &lvl
|
||||
|
||||
os.Remove("./crowdsec.log")
|
||||
os.Remove(expectedFile)
|
||||
|
||||
// Configure logging
|
||||
if err := types.SetDefaultLoggerConfig(cfg.LogMedia, cfg.LogDir, *cfg.LogLevel, cfg.LogMaxSize, cfg.LogMaxFiles, cfg.LogMaxAge, cfg.CompressLogs); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
)
|
||||
|
||||
func TestLogin(t *testing.T) {
|
||||
router, err := NewAPITest()
|
||||
router, config, err := NewAPITest()
|
||||
if err != nil {
|
||||
log.Fatalf("unable to run local API: %s", err)
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ func TestLogin(t *testing.T) {
|
|||
assert.Equal(t, "{\"code\":401,\"message\":\"input format error\"}", w.Body.String())
|
||||
|
||||
//Validate machine
|
||||
err = ValidateMachine("test")
|
||||
err = ValidateMachine("test", config.API.Server.DbConfig)
|
||||
if err != nil {
|
||||
log.Fatalln(err.Error())
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
)
|
||||
|
||||
func TestCreateMachine(t *testing.T) {
|
||||
router, err := NewAPITest()
|
||||
router, _, err := NewAPITest()
|
||||
if err != nil {
|
||||
log.Fatalf("unable to run local API: %s", err)
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ func TestCreateMachine(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCreateMachineWithForwardedFor(t *testing.T) {
|
||||
router, err := NewAPITestForwardedFor()
|
||||
router, config, err := NewAPITestForwardedFor()
|
||||
if err != nil {
|
||||
log.Fatalf("unable to run local API: %s", err)
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ func TestCreateMachineWithForwardedFor(t *testing.T) {
|
|||
assert.Equal(t, 201, w.Code)
|
||||
assert.Equal(t, "", w.Body.String())
|
||||
|
||||
ip, err := GetMachineIP(*MachineTest.MachineID)
|
||||
ip, err := GetMachineIP(*MachineTest.MachineID, config.API.Server.DbConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not get machine IP : %s", err)
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ func TestCreateMachineWithForwardedFor(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCreateMachineWithForwardedForNoConfig(t *testing.T) {
|
||||
router, err := NewAPITest()
|
||||
router, config, err := NewAPITest()
|
||||
if err != nil {
|
||||
log.Fatalf("unable to run local API: %s", err)
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ func TestCreateMachineWithForwardedForNoConfig(t *testing.T) {
|
|||
assert.Equal(t, 201, w.Code)
|
||||
assert.Equal(t, "", w.Body.String())
|
||||
|
||||
ip, err := GetMachineIP(*MachineTest.MachineID)
|
||||
ip, err := GetMachineIP(*MachineTest.MachineID, config.API.Server.DbConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not get machine IP : %s", err)
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ func TestCreateMachineWithForwardedForNoConfig(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCreateMachineWithoutForwardedFor(t *testing.T) {
|
||||
router, err := NewAPITestForwardedFor()
|
||||
router, config, err := NewAPITestForwardedFor()
|
||||
if err != nil {
|
||||
log.Fatalf("unable to run local API: %s", err)
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ func TestCreateMachineWithoutForwardedFor(t *testing.T) {
|
|||
assert.Equal(t, 201, w.Code)
|
||||
assert.Equal(t, "", w.Body.String())
|
||||
|
||||
ip, err := GetMachineIP(*MachineTest.MachineID)
|
||||
ip, err := GetMachineIP(*MachineTest.MachineID, config.API.Server.DbConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not get machine IP : %s", err)
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ func TestCreateMachineWithoutForwardedFor(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCreateMachineAlreadyExist(t *testing.T) {
|
||||
router, err := NewAPITest()
|
||||
router, _, err := NewAPITest()
|
||||
if err != nil {
|
||||
log.Fatalf("unable to run local API: %s", err)
|
||||
}
|
||||
|
|
9
pkg/apiserver/testutils.go
Normal file
9
pkg/apiserver/testutils.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
//go:build !windows
|
||||
|
||||
package apiserver
|
||||
|
||||
import "os"
|
||||
|
||||
func cleanFile(path string) {
|
||||
os.Remove(path)
|
||||
}
|
7
pkg/apiserver/testutils_windows.go
Normal file
7
pkg/apiserver/testutils_windows.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package apiserver
|
||||
|
||||
import "os"
|
||||
|
||||
func cleanFile(path string) {
|
||||
os.Remove(path)
|
||||
}
|
|
@ -3,6 +3,7 @@ package csconfig
|
|||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -17,9 +18,15 @@ func TestNormalLoad(t *testing.T) {
|
|||
}
|
||||
|
||||
_, err = NewConfig("./tests/xxx.yaml", false, false)
|
||||
if runtime.GOOS != "windows" {
|
||||
if fmt.Sprintf("%s", err) != "failed to read config file: open ./tests/xxx.yaml: no such file or directory" {
|
||||
t.Fatalf("unexpected error %s", err)
|
||||
}
|
||||
} else {
|
||||
if fmt.Sprintf("%s", err) != "failed to read config file: open ./tests/xxx.yaml: The system cannot find the file specified." {
|
||||
t.Fatalf("unexpected error %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = NewConfig("./tests/simulation.yaml", false, false)
|
||||
if !strings.HasPrefix(fmt.Sprintf("%s", err), "yaml: unmarshal errors:") {
|
||||
|
|
|
@ -3,6 +3,7 @@ package csconfig
|
|||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -39,17 +40,6 @@ func TestSimulationLoading(t *testing.T) {
|
|||
},
|
||||
expectedResult: &SimulationConfig{Simulation: new(bool)},
|
||||
},
|
||||
{
|
||||
name: "basic bad file name",
|
||||
Input: &Config{
|
||||
ConfigPaths: &ConfigurationPaths{
|
||||
SimulationFilePath: "./tests/xxx.yaml",
|
||||
DataDir: "./data",
|
||||
},
|
||||
Crowdsec: &CrowdsecServiceCfg{},
|
||||
},
|
||||
err: fmt.Sprintf("while reading '%s': open %s: no such file or directory", testXXFullPath, testXXFullPath),
|
||||
},
|
||||
{
|
||||
name: "basic nil config",
|
||||
Input: &Config{
|
||||
|
@ -84,6 +74,42 @@ func TestSimulationLoading(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
tests = append(tests, struct {
|
||||
name string
|
||||
Input *Config
|
||||
expectedResult *SimulationConfig
|
||||
err string
|
||||
}{
|
||||
name: "basic bad file name",
|
||||
Input: &Config{
|
||||
ConfigPaths: &ConfigurationPaths{
|
||||
SimulationFilePath: "./tests/xxx.yaml",
|
||||
DataDir: "./data",
|
||||
},
|
||||
Crowdsec: &CrowdsecServiceCfg{},
|
||||
},
|
||||
err: fmt.Sprintf("while reading '%s': open %s: The system cannot find the file specified.", testXXFullPath, testXXFullPath),
|
||||
})
|
||||
} else {
|
||||
tests = append(tests, struct {
|
||||
name string
|
||||
Input *Config
|
||||
expectedResult *SimulationConfig
|
||||
err string
|
||||
}{
|
||||
name: "basic bad file name",
|
||||
Input: &Config{
|
||||
ConfigPaths: &ConfigurationPaths{
|
||||
SimulationFilePath: "./tests/xxx.yaml",
|
||||
DataDir: "./data",
|
||||
},
|
||||
Crowdsec: &CrowdsecServiceCfg{},
|
||||
},
|
||||
err: fmt.Sprintf("while reading '%s': open %s: no such file or directory", testXXFullPath, testXXFullPath),
|
||||
})
|
||||
}
|
||||
|
||||
for idx, test := range tests {
|
||||
err := test.Input.LoadSimulation()
|
||||
if err == nil && test.err != "" {
|
||||
|
|
|
@ -4,16 +4,10 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"math"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
|
@ -259,16 +253,9 @@ func (pb *PluginBroker) loadNotificationPlugin(name string, binaryPath string) (
|
|||
return nil, err
|
||||
}
|
||||
log.Debugf("Executing plugin %s", binaryPath)
|
||||
cmd := exec.Command(binaryPath)
|
||||
if pb.pluginProcConfig.User != "" || pb.pluginProcConfig.Group != "" {
|
||||
if !(pb.pluginProcConfig.User != "" && pb.pluginProcConfig.Group != "") {
|
||||
return nil, errors.New("while getting process attributes: both plugin user and group must be set")
|
||||
}
|
||||
cmd.SysProcAttr, err = getProcessAttr(pb.pluginProcConfig.User, pb.pluginProcConfig.Group)
|
||||
cmd, err := pb.CreateCmd(binaryPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "while getting process attributes")
|
||||
}
|
||||
cmd.SysProcAttr.Credential.NoSetGroups = true
|
||||
return nil, err
|
||||
}
|
||||
pb.pluginMap[name] = &NotifierPlugin{}
|
||||
l := log.New()
|
||||
|
@ -365,43 +352,6 @@ func setRequiredFields(pluginCfg *PluginConfig) {
|
|||
|
||||
}
|
||||
|
||||
func pluginIsValid(path string) error {
|
||||
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 current user
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while getting current user")
|
||||
}
|
||||
currentUID, err := getUID(currentUser.Username)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while looking up the current uid")
|
||||
}
|
||||
stat := details.Sys().(*syscall.Stat_t)
|
||||
if stat.Uid != currentUID {
|
||||
return fmt.Errorf("plugin at %s is not owned by user '%s'", path, currentUser.Username)
|
||||
}
|
||||
|
||||
mode := details.Mode()
|
||||
perm := uint32(mode)
|
||||
if (perm & 00002) != 0 {
|
||||
return fmt.Errorf("plugin at %s is world writable, world writable plugins are invalid", path)
|
||||
}
|
||||
if (perm & 00020) != 0 {
|
||||
return fmt.Errorf("plugin at %s is group writable, group writable plugins are invalid", path)
|
||||
}
|
||||
if (mode & os.ModeSetgid) != 0 {
|
||||
return fmt.Errorf("plugin at %s has setgid permission, which is not allowed", 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)
|
||||
|
@ -418,62 +368,6 @@ func listFilesAtPath(path string) ([]string, error) {
|
|||
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 getUID(username string) (uint32, error) {
|
||||
u, err := user.Lookup(username)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
uid, err := strconv.ParseInt(u.Uid, 10, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if uid < 0 || uid > math.MaxInt32 {
|
||||
return 0, fmt.Errorf("out of bound uid")
|
||||
}
|
||||
return uint32(uid), nil
|
||||
}
|
||||
|
||||
func getGID(groupname string) (uint32, error) {
|
||||
g, err := user.LookupGroup(groupname)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
gid, err := strconv.ParseInt(g.Gid, 10, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if gid < 0 || gid > math.MaxInt32 {
|
||||
return 0, fmt.Errorf("out of bound gid")
|
||||
}
|
||||
return uint32(gid), nil
|
||||
}
|
||||
|
||||
func getProcessAttr(username string, groupname string) (*syscall.SysProcAttr, error) {
|
||||
uid, err := getUID(username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gid, err := getGID(groupname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &syscall.SysProcAttr{
|
||||
Credential: &syscall.Credential{
|
||||
Uid: uid,
|
||||
Gid: gid,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getUUID() (string, error) {
|
||||
uuidv4, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//go:build linux || freebsd || netbsd || openbsd || solaris || !windows
|
||||
|
||||
package csplugin
|
||||
|
||||
import (
|
||||
|
@ -6,7 +8,9 @@ import (
|
|||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -109,8 +113,8 @@ func TestListFilesAtPath(t *testing.T) {
|
|||
path: testPath,
|
||||
},
|
||||
want: []string{
|
||||
path.Join(testPath, "notification-gitter"),
|
||||
path.Join(testPath, "slack"),
|
||||
filepath.Join(testPath, "notification-gitter"),
|
||||
filepath.Join(testPath, "slack"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -136,6 +140,9 @@ func TestListFilesAtPath(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBrokerInit(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
@ -570,9 +577,11 @@ func buildDummyPlugin() {
|
|||
}
|
||||
|
||||
func setPluginPermTo(perm string) {
|
||||
if runtime.GOOS != "windows" {
|
||||
if err := exec.Command("chmod", perm, path.Join(testPath, "notification-dummy")).Run(); err != nil {
|
||||
log.Fatal(errors.Wrapf(err, "chmod 744 %s", path.Join(testPath, "notification-dummy")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setUp() {
|
||||
|
@ -580,14 +589,16 @@ func setUp() {
|
|||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_, err = os.Create(path.Join(dir, "slack"))
|
||||
f, err := os.Create(path.Join(dir, "slack"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_, err = os.Create(path.Join(dir, "notification-gitter"))
|
||||
f.Close()
|
||||
f, err = os.Create(path.Join(dir, "notification-gitter"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
err = os.Mkdir(path.Join(dir, "dummy_dir"), 0666)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
|
257
pkg/csplugin/broker_win_test.go
Normal file
257
pkg/csplugin/broker_win_test.go
Normal file
|
@ -0,0 +1,257 @@
|
|||
//go:build windows
|
||||
|
||||
package csplugin
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/tomb.v2"
|
||||
)
|
||||
|
||||
/*
|
||||
Due to the complexity of file permission modification with go on windows, we only test the basic behaviour the broker,
|
||||
not if it will actually reject plugins with invalid permissions
|
||||
*/
|
||||
|
||||
var testPath string
|
||||
|
||||
func TestGetPluginNameAndTypeFromPath(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.exe",
|
||||
},
|
||||
want: "",
|
||||
want1: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid plugin name, multiple dash",
|
||||
args: args{
|
||||
path: ".\\tests\\notification-instant-slack.exe",
|
||||
},
|
||||
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 TestListFilesAtPath(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{
|
||||
filepath.Join(testPath, "notification-gitter"),
|
||||
filepath.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 TestBrokerInit(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
action func()
|
||||
errContains string
|
||||
wantErr bool
|
||||
procCfg csconfig.PluginCfg
|
||||
}{
|
||||
{
|
||||
name: "valid config",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "no plugin dir",
|
||||
wantErr: true,
|
||||
errContains: "The system cannot find the file specified.",
|
||||
action: tearDown,
|
||||
},
|
||||
{
|
||||
name: "no plugin binary",
|
||||
wantErr: true,
|
||||
errContains: "binary for plugin dummy_default not found",
|
||||
action: func() {
|
||||
err := os.Remove(path.Join(testPath, "notification-dummy.exe"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
defer tearDown()
|
||||
buildDummyPlugin()
|
||||
if test.action != nil {
|
||||
test.action()
|
||||
}
|
||||
pb := PluginBroker{}
|
||||
profiles := csconfig.NewDefaultConfig().API.Server.Profiles
|
||||
profiles = append(profiles, &csconfig.ProfileCfg{
|
||||
Notifications: []string{"dummy_default"},
|
||||
})
|
||||
err := pb.Init(&test.procCfg, profiles, &csconfig.ConfigurationPaths{
|
||||
PluginDir: testPath,
|
||||
NotificationDir: "./tests/notifications",
|
||||
})
|
||||
defer pb.Kill()
|
||||
if test.wantErr {
|
||||
assert.ErrorContains(t, err, test.errContains)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBrokerRun(t *testing.T) {
|
||||
buildDummyPlugin()
|
||||
defer tearDown()
|
||||
procCfg := csconfig.PluginCfg{}
|
||||
pb := PluginBroker{}
|
||||
profiles := csconfig.NewDefaultConfig().API.Server.Profiles
|
||||
profiles = append(profiles, &csconfig.ProfileCfg{
|
||||
Notifications: []string{"dummy_default"},
|
||||
})
|
||||
err := pb.Init(&procCfg, profiles, &csconfig.ConfigurationPaths{
|
||||
PluginDir: testPath,
|
||||
NotificationDir: "./tests/notifications",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
tomb := tomb.Tomb{}
|
||||
go pb.Run(&tomb)
|
||||
defer pb.Kill()
|
||||
|
||||
assert.NoFileExists(t, "./out")
|
||||
defer os.Remove("./out")
|
||||
|
||||
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
||||
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
||||
time.Sleep(time.Second * 4)
|
||||
|
||||
assert.FileExists(t, ".\\out")
|
||||
assert.Equal(t, types.GetLineCountForFile(".\\out"), 2)
|
||||
}
|
||||
|
||||
func buildDummyPlugin() {
|
||||
dir, err := os.MkdirTemp(".\\tests", "cs_plugin_test")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cmd := exec.Command("go", "build", "-o", path.Join(dir, "notification-dummy.exe"), "../../plugins/notifications/dummy/")
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
testPath = dir
|
||||
}
|
||||
|
||||
func setUp() {
|
||||
dir, err := os.MkdirTemp("./", "cs_plugin_test")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
f, err := os.Create(path.Join(dir, "slack"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
f, err = os.Create(path.Join(dir, "notification-gitter"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
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)
|
||||
}
|
||||
}
|
150
pkg/csplugin/utils.go
Normal file
150
pkg/csplugin/utils.go
Normal file
|
@ -0,0 +1,150 @@
|
|||
//go:build linux || freebsd || netbsd || openbsd || solaris || !windows
|
||||
|
||||
package csplugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"math"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func CheckCredential(uid int, gid int) *syscall.SysProcAttr {
|
||||
return &syscall.SysProcAttr{
|
||||
Credential: &syscall.Credential{
|
||||
Uid: uint32(uid),
|
||||
Gid: uint32(gid),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (pb *PluginBroker) CreateCmd(binaryPath string) (*exec.Cmd, error) {
|
||||
var err error
|
||||
cmd := exec.Command(binaryPath)
|
||||
if pb.pluginProcConfig.User != "" || pb.pluginProcConfig.Group != "" {
|
||||
if !(pb.pluginProcConfig.User != "" && pb.pluginProcConfig.Group != "") {
|
||||
return nil, errors.New("while getting process attributes: both plugin user and group must be set")
|
||||
}
|
||||
cmd.SysProcAttr, err = getProcessAttr(pb.pluginProcConfig.User, pb.pluginProcConfig.Group)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "while getting process attributes")
|
||||
}
|
||||
cmd.SysProcAttr.Credential.NoSetGroups = true
|
||||
}
|
||||
return cmd, err
|
||||
}
|
||||
|
||||
func getUID(username string) (uint32, error) {
|
||||
u, err := user.Lookup(username)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
uid, err := strconv.ParseInt(u.Uid, 10, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if uid < 0 || uid > math.MaxInt32 {
|
||||
return 0, fmt.Errorf("out of bound uid")
|
||||
}
|
||||
return uint32(uid), nil
|
||||
}
|
||||
|
||||
func getGID(groupname string) (uint32, error) {
|
||||
g, err := user.LookupGroup(groupname)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
gid, err := strconv.ParseInt(g.Gid, 10, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if gid < 0 || gid > math.MaxInt32 {
|
||||
return 0, fmt.Errorf("out of bound gid")
|
||||
}
|
||||
return uint32(gid), 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 getProcessAttr(username string, groupname string) (*syscall.SysProcAttr, error) {
|
||||
u, err := user.Lookup(username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g, err := user.LookupGroup(groupname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uid, err := strconv.ParseInt(u.Uid, 10, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if uid < 0 && uid > math.MaxInt32 {
|
||||
return nil, fmt.Errorf("out of bound uid")
|
||||
}
|
||||
gid, err := strconv.ParseInt(g.Gid, 10, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if gid < 0 && gid > math.MaxInt32 {
|
||||
return nil, fmt.Errorf("out of bound gid")
|
||||
}
|
||||
return &syscall.SysProcAttr{
|
||||
Credential: &syscall.Credential{
|
||||
Uid: uint32(uid),
|
||||
Gid: uint32(gid),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func pluginIsValid(path string) error {
|
||||
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 current user
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while getting current user")
|
||||
}
|
||||
currentUID, err := getUID(currentUser.Username)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while looking up the current uid")
|
||||
}
|
||||
stat := details.Sys().(*syscall.Stat_t)
|
||||
if stat.Uid != currentUID {
|
||||
return fmt.Errorf("plugin at %s is not owned by user '%s'", path, currentUser.Username)
|
||||
}
|
||||
|
||||
mode := details.Mode()
|
||||
perm := uint32(mode)
|
||||
if (perm & 00002) != 0 {
|
||||
return fmt.Errorf("plugin at %s is world writable, world writable plugins are invalid", path)
|
||||
}
|
||||
if (perm & 00020) != 0 {
|
||||
return fmt.Errorf("plugin at %s is group writable, group writable plugins are invalid", path)
|
||||
}
|
||||
if (mode & os.ModeSetgid) != 0 {
|
||||
return fmt.Errorf("plugin at %s has setgid permission, which is not allowed", path)
|
||||
}
|
||||
return nil
|
||||
}
|
242
pkg/csplugin/utils_windows.go
Normal file
242
pkg/csplugin/utils_windows.go
Normal file
|
@ -0,0 +1,242 @@
|
|||
//go:build windows
|
||||
|
||||
package csplugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
advapi32 = syscall.NewLazyDLL("advapi32.dll")
|
||||
|
||||
procGetAce = advapi32.NewProc("GetAce")
|
||||
)
|
||||
|
||||
type AclSizeInformation struct {
|
||||
AceCount uint32
|
||||
AclBytesInUse uint32
|
||||
AclBytesFree uint32
|
||||
}
|
||||
|
||||
type Acl struct {
|
||||
AclRevision uint8
|
||||
Sbz1 uint8
|
||||
AclSize uint16
|
||||
AceCount uint16
|
||||
Sbz2 uint16
|
||||
}
|
||||
|
||||
type AccessAllowedAce struct {
|
||||
AceType uint8
|
||||
AceFlags uint8
|
||||
AceSize uint16
|
||||
AccessMask uint32
|
||||
SidStart uint32
|
||||
}
|
||||
|
||||
const ACCESS_ALLOWED_ACE_TYPE = 0
|
||||
const ACCESS_DENIED_ACE_TYPE = 1
|
||||
|
||||
func CheckPerms(path string) error {
|
||||
log.Debugf("checking permissions of %s\n", path)
|
||||
|
||||
systemSid, err := windows.CreateWellKnownSid(windows.WELL_KNOWN_SID_TYPE(windows.WinLocalSystemSid))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while creating SYSTEM well known sid")
|
||||
}
|
||||
|
||||
adminSid, err := windows.CreateWellKnownSid(windows.WELL_KNOWN_SID_TYPE(windows.WinBuiltinAdministratorsSid))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while creating built-in Administrators well known sid")
|
||||
}
|
||||
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while getting current user")
|
||||
}
|
||||
|
||||
currentUserSid, _, _, err := windows.LookupSID("", currentUser.Username)
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while looking up current user sid")
|
||||
}
|
||||
|
||||
sd, err := windows.GetNamedSecurityInfo(path, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while getting owner security info")
|
||||
}
|
||||
if !sd.IsValid() {
|
||||
return errors.New("security descriptor is invalid")
|
||||
}
|
||||
owner, _, err := sd.Owner()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while getting owner")
|
||||
}
|
||||
if !owner.IsValid() {
|
||||
return errors.New("owner is invalid")
|
||||
}
|
||||
|
||||
if !owner.Equals(systemSid) && !owner.Equals(currentUserSid) && !owner.Equals(adminSid) {
|
||||
return fmt.Errorf("plugin at %s is not owned by SYSTEM, Administrators or by current user, but by %s", path, owner.String())
|
||||
}
|
||||
|
||||
dacl, _, err := sd.DACL()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while getting DACL")
|
||||
}
|
||||
|
||||
if dacl == nil {
|
||||
return fmt.Errorf("no DACL found on plugin, meaning fully permissive access on plugin %s", path)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while looking up current user sid")
|
||||
}
|
||||
|
||||
rs := reflect.ValueOf(dacl).Elem()
|
||||
|
||||
/*
|
||||
For reference, the structure of the ACL type is:
|
||||
type ACL struct {
|
||||
aclRevision byte
|
||||
sbz1 byte
|
||||
aclSize uint16
|
||||
aceCount uint16
|
||||
sbz2 uint16
|
||||
}
|
||||
As the field are not exported, we have to use reflection to access them, this should not be an issue as the structure won't (probably) change any time soon.
|
||||
*/
|
||||
aceCount := rs.Field(3).Uint()
|
||||
|
||||
for i := uint64(0); i < aceCount; i++ {
|
||||
ace := &AccessAllowedAce{}
|
||||
ret, _, _ := procGetAce.Call(uintptr(unsafe.Pointer(dacl)), uintptr(i), uintptr(unsafe.Pointer(&ace)))
|
||||
if ret == 0 {
|
||||
return errors.Wrap(windows.GetLastError(), "while getting ACE")
|
||||
}
|
||||
log.Debugf("ACE %d: %+v\n", i, ace)
|
||||
|
||||
if ace.AceType == ACCESS_DENIED_ACE_TYPE {
|
||||
continue
|
||||
}
|
||||
aceSid := (*windows.SID)(unsafe.Pointer(&ace.SidStart))
|
||||
|
||||
if aceSid.Equals(systemSid) || aceSid.Equals(adminSid) {
|
||||
log.Debugf("Not checking permission for well-known SID %s", aceSid.String())
|
||||
continue
|
||||
}
|
||||
|
||||
if aceSid.Equals(currentUserSid) {
|
||||
log.Debugf("Not checking permission for current user %s", currentUser.Username)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debugf("Checking permission for SID %s", aceSid.String())
|
||||
denyMask := ^(windows.FILE_GENERIC_READ | windows.FILE_GENERIC_EXECUTE)
|
||||
if ace.AccessMask&uint32(denyMask) != 0 {
|
||||
return fmt.Errorf("only SYSTEM, Administrators or the user currently running crowdsec can have more than read/execute on plugin %s", path)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getProcessAtr() (*syscall.SysProcAttr, error) {
|
||||
var procToken, token windows.Token
|
||||
|
||||
proc := windows.CurrentProcess()
|
||||
defer windows.CloseHandle(proc)
|
||||
|
||||
err := windows.OpenProcessToken(proc, windows.TOKEN_DUPLICATE|windows.TOKEN_ADJUST_DEFAULT|
|
||||
windows.TOKEN_QUERY|windows.TOKEN_ASSIGN_PRIMARY|windows.TOKEN_ADJUST_GROUPS|windows.TOKEN_ADJUST_PRIVILEGES, &procToken)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "while opening process token")
|
||||
}
|
||||
defer procToken.Close()
|
||||
|
||||
err = windows.DuplicateTokenEx(procToken, 0, nil, windows.SecurityImpersonation,
|
||||
windows.TokenPrimary, &token)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "while duplicating token")
|
||||
}
|
||||
|
||||
//Remove all privileges from the token
|
||||
|
||||
err = windows.AdjustTokenPrivileges(token, true, nil, 0, nil, nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "while adjusting token privileges")
|
||||
}
|
||||
|
||||
//Run the plugin as a medium integrity level process
|
||||
//For some reasons, low level integrity don't work, the plugin and crowdsec cannot communicate over the TCP socket
|
||||
sid, err := windows.CreateWellKnownSid(windows.WELL_KNOWN_SID_TYPE(windows.WinMediumLabelSid))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tml := &windows.Tokenmandatorylabel{}
|
||||
tml.Label.Attributes = windows.SE_GROUP_INTEGRITY
|
||||
tml.Label.Sid = sid
|
||||
|
||||
err = windows.SetTokenInformation(token, windows.TokenIntegrityLevel,
|
||||
(*byte)(unsafe.Pointer(tml)), tml.Size())
|
||||
if err != nil {
|
||||
token.Close()
|
||||
return nil, errors.Wrapf(err, "while setting token information")
|
||||
}
|
||||
|
||||
return &windows.SysProcAttr{
|
||||
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
|
||||
Token: syscall.Token(token),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (pb *PluginBroker) CreateCmd(binaryPath string) (*exec.Cmd, error) {
|
||||
var err error
|
||||
cmd := exec.Command(binaryPath)
|
||||
cmd.SysProcAttr, err = getProcessAtr()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "while getting process attributes")
|
||||
}
|
||||
return cmd, err
|
||||
}
|
||||
|
||||
func getPluginTypeAndSubtypeFromPath(path string) (string, string, error) {
|
||||
pluginFileName := strings.TrimSuffix(filepath.Base(path), filepath.Ext(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 pluginIsValid(path string) error {
|
||||
var err error
|
||||
|
||||
// check if it exists
|
||||
if _, 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
|
||||
err = CheckPerms(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -3,6 +3,7 @@ package csplugin
|
|||
import (
|
||||
"context"
|
||||
"log"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -45,6 +46,9 @@ func listenChannelWithTimeout(ctx context.Context, channel chan string) error {
|
|||
}
|
||||
|
||||
func TestPluginWatcherInterval(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows because timing is not reliable")
|
||||
}
|
||||
pw := PluginWatcher{}
|
||||
alertsByPluginName := make(map[string][]*models.Alert)
|
||||
testTomb := tomb.Tomb{}
|
||||
|
@ -74,6 +78,9 @@ func TestPluginWatcherInterval(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPluginAlertCountWatcher(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows because timing is not reliable")
|
||||
}
|
||||
pw := PluginWatcher{}
|
||||
alertsByPluginName := make(map[string][]*models.Alert)
|
||||
configs := map[string]PluginConfig{
|
||||
|
|
|
@ -193,10 +193,10 @@ func (t *HubTestItem) InstallHub() error {
|
|||
//return fmt.Errorf("parser '%s' doesn't exist in the hub and doesn't appear to be a custom one.", parser)
|
||||
}
|
||||
|
||||
customParserPathSplit := strings.Split(customParserPath, "/")
|
||||
customParserName := customParserPathSplit[len(customParserPathSplit)-1]
|
||||
customParserPathSplit, customParserName := filepath.Split(customParserPath)
|
||||
// because path is parsers/<stage>/<author>/parser.yaml and we wan't the stage
|
||||
customParserStage := customParserPathSplit[len(customParserPathSplit)-3]
|
||||
splittedPath := strings.Split(customParserPathSplit, string(os.PathSeparator))
|
||||
customParserStage := splittedPath[len(splittedPath)-3]
|
||||
|
||||
// check if stage exist
|
||||
hubStagePath := filepath.Join(t.HubPath, fmt.Sprintf("parsers/%s", customParserStage))
|
||||
|
|
|
@ -230,11 +230,11 @@ func testTaintItem(cfg *csconfig.Hub, t *testing.T, item Item) {
|
|||
if err != nil {
|
||||
t.Fatalf("(taint) opening %s (%s) : %s", item.LocalPath, item.Name, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err = f.WriteString("tainted"); err != nil {
|
||||
t.Fatalf("tainting %s : %s", item.Name, err)
|
||||
}
|
||||
f.Close()
|
||||
//Local sync and check status
|
||||
if err, _ := LocalSync(cfg); err != nil {
|
||||
t.Fatalf("taint: failed to run localSync : %s", err)
|
||||
|
@ -398,8 +398,9 @@ func (t *mockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|||
|
||||
func fileToStringX(path string) string {
|
||||
if f, err := os.Open(path); err == nil {
|
||||
defer f.Close()
|
||||
if data, err := io.ReadAll(f); err == nil {
|
||||
return string(data)
|
||||
return strings.ReplaceAll(string(data), "\r\n", "\n")
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
@ -233,6 +233,7 @@ func DownloadDataIfNeeded(hub *csconfig.Hub, target Item, force bool) error {
|
|||
if itemFile, err = os.Open(itemFilePath); err != nil {
|
||||
return errors.Wrapf(err, "while opening %s", itemFilePath)
|
||||
}
|
||||
defer itemFile.Close()
|
||||
if err = downloadData(dataFolder, force, itemFile); err != nil {
|
||||
return errors.Wrapf(err, "while downloading data for %s", itemFilePath)
|
||||
}
|
||||
|
|
|
@ -47,9 +47,10 @@ func parser_visit(path string, f os.FileInfo, err error) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
subs := strings.Split(path, "/")
|
||||
subs := strings.Split(path, string(os.PathSeparator))
|
||||
|
||||
log.Tracef("path:%s, hubdir:%s, installdir:%s", path, hubdir, installdir)
|
||||
log.Tracef("subs:%v", subs)
|
||||
/*we're in hub (~/.hub/hub/)*/
|
||||
if strings.HasPrefix(path, hubdir) {
|
||||
log.Tracef("in hub dir")
|
||||
|
@ -78,7 +79,7 @@ func parser_visit(path string, f os.FileInfo, err error) error {
|
|||
ftype = subs[len(subs)-3]
|
||||
fauthor = ""
|
||||
} else {
|
||||
return fmt.Errorf("File '%s' is not from hub '%s' nor from the configuration directory '%s'", path, hubdir, installdir)
|
||||
return fmt.Errorf("file '%s' is not from hub '%s' nor from the configuration directory '%s'", path, hubdir, installdir)
|
||||
}
|
||||
|
||||
log.Tracef("stage:%s ftype:%s", stage, ftype)
|
||||
|
@ -134,8 +135,7 @@ func parser_visit(path string, f os.FileInfo, err error) error {
|
|||
target.Local = true
|
||||
target.LocalPath = path
|
||||
target.UpToDate = true
|
||||
x := strings.Split(path, "/")
|
||||
target.FileName = x[len(x)-1]
|
||||
_, target.FileName = filepath.Split(path)
|
||||
|
||||
hubIdx[ftype][fname] = target
|
||||
return nil
|
||||
|
@ -161,9 +161,10 @@ func parser_visit(path string, f os.FileInfo, err error) error {
|
|||
continue
|
||||
}
|
||||
//wrong file
|
||||
if v.Name+".yaml" != fauthor+"/"+fname && v.Name+".yml" != fauthor+"/"+fname {
|
||||
if CheckName(v.Name, fauthor, fname) {
|
||||
continue
|
||||
}
|
||||
|
||||
if path == hubdir+"/"+v.RemotePath {
|
||||
log.Tracef("marking %s as downloaded", v.Name)
|
||||
v.Downloaded = true
|
||||
|
@ -171,9 +172,7 @@ func parser_visit(path string, f os.FileInfo, err error) error {
|
|||
} else {
|
||||
//wrong file
|
||||
//<type>/<stage>/<author>/<name>.yaml
|
||||
if !strings.HasSuffix(hubpath, v.RemotePath) {
|
||||
//log.Printf("wrong file %s %s", hubpath, spew.Sdump(v))
|
||||
|
||||
if CheckSuffix(hubpath, v.RemotePath) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
@ -204,8 +203,7 @@ func parser_visit(path string, f os.FileInfo, err error) error {
|
|||
/*if we're walking the hub, present file doesn't means installed file*/
|
||||
v.Installed = true
|
||||
v.LocalHash = sha
|
||||
x := strings.Split(path, "/")
|
||||
target.FileName = x[len(x)-1]
|
||||
_, target.FileName = filepath.Split(path)
|
||||
} else {
|
||||
v.Downloaded = true
|
||||
v.LocalHash = sha
|
||||
|
@ -229,8 +227,7 @@ func parser_visit(path string, f os.FileInfo, err error) error {
|
|||
v.LocalVersion = "?"
|
||||
v.Tainted = true
|
||||
v.LocalHash = sha
|
||||
x := strings.Split(path, "/")
|
||||
target.FileName = x[len(x)-1]
|
||||
_, target.FileName = filepath.Split(path)
|
||||
|
||||
}
|
||||
//update the entry if appropriate
|
||||
|
|
23
pkg/cwhub/path_separator_windows.go
Normal file
23
pkg/cwhub/path_separator_windows.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package cwhub
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func CheckSuffix(hubpath string, remotePath string) bool {
|
||||
newPath := filepath.ToSlash(hubpath)
|
||||
if !strings.HasSuffix(newPath, remotePath) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func CheckName(vname string, fauthor string, fname string) bool {
|
||||
if vname+".yaml" != fauthor+"/"+fname && vname+".yml" != fauthor+"/"+fname {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
24
pkg/cwhub/pathseparator.go
Normal file
24
pkg/cwhub/pathseparator.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
//go:build linux || freebsd || netbsd || openbsd || solaris || !windows
|
||||
// +build linux freebsd netbsd openbsd solaris !windows
|
||||
|
||||
package cwhub
|
||||
|
||||
import "strings"
|
||||
|
||||
const PathSeparator = "/"
|
||||
|
||||
func CheckSuffix(hubpath string, remotePath string) bool {
|
||||
if !strings.HasSuffix(hubpath, remotePath) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func CheckName(vname string, fauthor string, fname string) bool {
|
||||
if vname+".yaml" != fauthor+"/"+fname && vname+".yml" != fauthor+"/"+fname {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -182,6 +182,9 @@ func TestLongRunningQPS(t *testing.T) {
|
|||
t.Skip("low resolution time.Sleep invalidates test (golang.org/issue/14183)")
|
||||
return
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("test is unreliable on windows")
|
||||
}
|
||||
|
||||
// The test runs for a few seconds executing many requests and then checks
|
||||
// that overall number of requests is reasonable.
|
||||
|
|
|
@ -100,7 +100,7 @@ func Clone(a, b interface{}) error {
|
|||
}
|
||||
|
||||
func WriteStackTrace(iErr interface{}) string {
|
||||
tmpfile, err := ioutil.TempFile("/tmp/", "crowdsec-crash.*.txt")
|
||||
tmpfile, err := ioutil.TempFile("", "crowdsec-crash.*.txt")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -3,3 +3,4 @@
|
|||
|
||||
Make=gmake
|
||||
|
||||
$(info building for FreeBSD)
|
|
@ -2,3 +2,4 @@
|
|||
|
||||
MAKE=make
|
||||
|
||||
$(info Building for linux)
|
18
platform/unix_common.mk
Normal file
18
platform/unix_common.mk
Normal file
|
@ -0,0 +1,18 @@
|
|||
|
||||
RM=rm -rf
|
||||
CP=cp
|
||||
CPR=cp -r
|
||||
MKDIR=mkdir -p
|
||||
|
||||
GO_MAJOR_VERSION = $(shell go version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1)
|
||||
GO_MINOR_VERSION = $(shell go version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2)
|
||||
|
||||
BUILD_GOVERSION="$(shell go version | cut -d " " -f3 | sed -E 's/[go]+//g')"
|
||||
|
||||
#Current versioning information from env
|
||||
BUILD_VERSION?="$(shell git describe --tags $$(git rev-list --tags --max-count=1))"
|
||||
BUILD_CODENAME="alphaga"
|
||||
BUILD_TIMESTAMP=$(shell date +%F"_"%T)
|
||||
BUILD_TAG?="$(shell git rev-parse HEAD)"
|
||||
DEFAULT_CONFIGDIR?=/etc/crowdsec
|
||||
DEFAULT_DATADIR?=/var/lib/crowdsec/data
|
32
platform/windows.mk
Normal file
32
platform/windows.mk
Normal file
|
@ -0,0 +1,32 @@
|
|||
# Windows specific
|
||||
#
|
||||
|
||||
MAKE=make
|
||||
GOOS=windows
|
||||
PREFIX=$(shell $$env:TEMP)
|
||||
|
||||
GO_MAJOR_VERSION ?= $(shell (go env GOVERSION).replace("go","").split(".")[0])
|
||||
GO_MINOR_VERSION ?= $(shell (go env GOVERSION).replace("go","").split(".")[1])
|
||||
MINIMUM_SUPPORTED_GO_MAJOR_VERSION = 1
|
||||
MINIMUM_SUPPORTED_GO_MINOR_VERSION = 17
|
||||
GO_VERSION_VALIDATION_ERR_MSG = Golang version ($(BUILD_GOVERSION)) is not supported, please use least $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION).$(MINIMUM_SUPPORTED_GO_MINOR_VERSION)
|
||||
#Current versioning information from env
|
||||
#BUILD_VERSION?=$(shell (Invoke-WebRequest -UseBasicParsing -Uri https://api.github.com/repos/crowdsecurity/crowdsec/releases/latest).Content | jq -r '.tag_name')
|
||||
#hardcode it till i find a workaround
|
||||
BUILD_VERSION?=$(shell git describe --tags $$(git rev-list --tags --max-count=1))
|
||||
BUILD_GOVERSION?=$(shell (go env GOVERSION).replace("go",""))
|
||||
BUILD_CODENAME?=alphaga
|
||||
BUILD_TIMESTAMP?=$(shell Get-Date -Format "yyyy-MM-dd_HH:mm:ss")
|
||||
BUILD_TAG?=$(shell git rev-parse HEAD)
|
||||
DEFAULT_CONFIGDIR?=C:\\ProgramData\\CrowdSec\\config
|
||||
DEFAULT_DATADIR?=C:\\ProgramData\\CrowdSec\\data
|
||||
|
||||
#please tell me there is a better way to completly ignore errors when trying to delete a file....
|
||||
RM=Remove-Item -ErrorAction Ignore -Recurse
|
||||
CP=Copy-Item
|
||||
CPR=Copy-Item -Recurse
|
||||
MKDIR=New-Item -ItemType directory
|
||||
WIN_IGNORE_ERR=; exit 0
|
||||
|
||||
|
||||
$(info Building for windows)
|
|
@ -1,13 +1,20 @@
|
|||
ifeq ($(OS),Windows_NT)
|
||||
SHELL := pwsh.exe
|
||||
.SHELLFLAGS := -NoProfile -Command
|
||||
EXT=.exe
|
||||
endif
|
||||
|
||||
|
||||
# Go parameters
|
||||
GOCMD=go
|
||||
GOBUILD=$(GOCMD) build
|
||||
GOCLEAN=$(GOCMD) clean
|
||||
GOTEST=$(GOCMD) test
|
||||
GOGET=$(GOCMD) get
|
||||
BINARY_NAME=notification-dummy
|
||||
BINARY_NAME=notification-dummy$(EXT)
|
||||
|
||||
clean:
|
||||
@$(RM) "$(BINARY_NAME)"
|
||||
@$(RM) $(BINARY_NAME) $(WIN_IGNORE_ERR)
|
||||
|
||||
build: clean
|
||||
@$(GOBUILD) $(LD_OPTS) -o $(BINARY_NAME) -v
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
ifeq ($(OS),Windows_NT)
|
||||
SHELL := pwsh.exe
|
||||
.SHELLFLAGS := -NoProfile -Command
|
||||
EXT=.exe
|
||||
endif
|
||||
|
||||
|
||||
# Go parameters
|
||||
GOCMD=go
|
||||
GOBUILD=$(GOCMD) build
|
||||
GOCLEAN=$(GOCMD) clean
|
||||
GOTEST=$(GOCMD) test
|
||||
GOGET=$(GOCMD) get
|
||||
BINARY_NAME=notification-email
|
||||
BINARY_NAME=notification-email$(EXT)
|
||||
|
||||
clean:
|
||||
@$(RM) "$(BINARY_NAME)"
|
||||
@$(RM) $(BINARY_NAME) $(WIN_IGNORE_ERR)
|
||||
|
||||
build: clean
|
||||
@$(GOBUILD) $(LD_OPTS) -o $(BINARY_NAME) -v
|
||||
|
|
|
@ -1,16 +1,23 @@
|
|||
ifeq ($(OS),Windows_NT)
|
||||
SHELL := pwsh.exe
|
||||
.SHELLFLAGS := -NoProfile -Command
|
||||
EXT=.exe
|
||||
endif
|
||||
|
||||
|
||||
# Go parameters
|
||||
GOCMD=go
|
||||
GOBUILD=$(GOCMD) build
|
||||
GOCLEAN=$(GOCMD) clean
|
||||
GOTEST=$(GOCMD) test
|
||||
GOGET=$(GOCMD) get
|
||||
BINARY_NAME=notification-http
|
||||
BINARY_NAME=notification-http$(EXT)
|
||||
|
||||
clean:
|
||||
@$(RM) "$(BINARY_NAME)"
|
||||
@$(RM) $(BINARY_NAME) $(WIN_IGNORE_ERR)
|
||||
|
||||
build: clean
|
||||
@$(GOBUILD) $(LD_OPTS) -o $(BINARY_NAME) -v
|
||||
$(GOBUILD) $(LD_OPTS) -o $(BINARY_NAME) -v
|
||||
|
||||
static: clean
|
||||
$(GOBUILD) $(LD_OPTS_STATIC) -o $(BINARY_NAME) -v -a -tags netgo
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
ifeq ($(OS),Windows_NT)
|
||||
SHELL := pwsh.exe
|
||||
.SHELLFLAGS := -NoProfile -Command
|
||||
EXT=.exe
|
||||
endif
|
||||
|
||||
# Go parameters
|
||||
GOCMD=go
|
||||
GOBUILD=$(GOCMD) build
|
||||
GOCLEAN=$(GOCMD) clean
|
||||
GOTEST=$(GOCMD) test
|
||||
GOGET=$(GOCMD) get
|
||||
BINARY_NAME=notification-slack
|
||||
BINARY_NAME=notification-slack$(EXT)
|
||||
|
||||
build: clean
|
||||
@$(GOBUILD) $(LD_OPTS) -o $(BINARY_NAME) -v
|
||||
|
||||
clean:
|
||||
@$(RM) "$(BINARY_NAME)"
|
||||
@$(RM) $(BINARY_NAME) $(WIN_IGNORE_ERR)
|
||||
|
||||
|
||||
static: clean
|
||||
|
|
|
@ -1,16 +1,24 @@
|
|||
ifeq ($(OS),Windows_NT)
|
||||
SHELL := pwsh.exe
|
||||
.SHELLFLAGS := -NoProfile -Command
|
||||
EXT=.exe
|
||||
endif
|
||||
|
||||
|
||||
|
||||
# Go parameters
|
||||
GOCMD=go
|
||||
GOBUILD=$(GOCMD) build
|
||||
GOCLEAN=$(GOCMD) clean
|
||||
GOTEST=$(GOCMD) test
|
||||
GOGET=$(GOCMD) get
|
||||
BINARY_NAME=notification-splunk
|
||||
BINARY_NAME=notification-splunk$(EXT)
|
||||
|
||||
build: clean
|
||||
@$(GOBUILD) $(LD_OPTS) -o $(BINARY_NAME) -v
|
||||
$(GOBUILD) $(LD_OPTS) -o $(BINARY_NAME) -v
|
||||
|
||||
clean:
|
||||
@$(RM) "$(BINARY_NAME)"
|
||||
@$(RM) $(BINARY_NAME) $(WIN_IGNORE_ERR)
|
||||
|
||||
static: clean
|
||||
$(GOBUILD) $(LD_OPTS_STATIC) -o $(BINARY_NAME) -v -a -tags netgo
|
||||
|
|
19
scripts/check_go_version.ps1
Normal file
19
scripts/check_go_version.ps1
Normal file
|
@ -0,0 +1,19 @@
|
|||
##This must be called with $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION) $(MINIMUM_SUPPORTED_GO_MINOR_VERSION) in this order
|
||||
$min_major=$args[0]
|
||||
$min_minor=$args[1]
|
||||
$goversion = (go env GOVERSION).replace("go","").split(".")
|
||||
$goversion_major=$goversion[0]
|
||||
$goversion_minor=$goversion[1]
|
||||
$err_msg="Golang version $goversion_major.$goversion_minor is not supported, please use least $min_major.$min_minor"
|
||||
|
||||
if ( $goversion_major -gt $min_major ) {
|
||||
exit 0;
|
||||
}
|
||||
elseif ($goversion_major -lt $min_major) {
|
||||
Write-Output $err_msg;
|
||||
exit 1;
|
||||
}
|
||||
elseif ($goversion_minor -lt $min_minor) {
|
||||
Write-Output $(GO_VERSION_VALIDATION_ERR_MSG);
|
||||
exit 1;
|
||||
}
|
90
scripts/test_env.ps1
Normal file
90
scripts/test_env.ps1
Normal file
|
@ -0,0 +1,90 @@
|
|||
#this is is straight up conversion of test_env.sh, not pretty but does the job
|
||||
|
||||
param (
|
||||
[string]$base = ".\tests",
|
||||
[switch]$help = $false
|
||||
)
|
||||
|
||||
function show_help() {
|
||||
Write-Output ".\test_env.ps1 -d tests #creates test env in .\tests"
|
||||
}
|
||||
|
||||
function create_arbo() {
|
||||
$null = New-Item -ItemType Directory $data_dir
|
||||
$null = New-Item -ItemType Directory $log_dir
|
||||
$null = New-Item -ItemType Directory $config_dir
|
||||
$null = New-Item -ItemType Directory $parser_dir
|
||||
$null = New-Item -ItemType Directory $parser_s00
|
||||
$null = New-Item -ItemType Directory $parser_s01
|
||||
$null = New-Item -ItemType Directory $parser_s02
|
||||
$null = New-Item -ItemType Directory $scenarios_dir
|
||||
$null = New-Item -ItemType Directory $postoverflows_dir
|
||||
$null = New-Item -ItemType Directory $cscli_dir
|
||||
$null = New-Item -ItemType Directory $hub_dir
|
||||
$null = New-Item -ItemType Directory $config_dir\$notif_dir
|
||||
$null = New-Item -ItemType Directory $base\$plugins_dir
|
||||
}
|
||||
|
||||
function copy_file() {
|
||||
$null = Copy-Item ".\config\profiles.yaml" $config_dir
|
||||
$null = Copy-Item ".\config\simulation.yaml" $config_dir
|
||||
$null = Copy-Item ".\cmd\crowdsec\crowdsec.exe" $base
|
||||
$null = Copy-Item ".\cmd\crowdsec-cli\cscli.exe" $base
|
||||
$null = Copy-Item -Recurse ".\config\patterns" $config_dir
|
||||
$null = Copy-Item ".\config\acquis.yaml" $config_dir
|
||||
$null = New-Item -ItemType File $config_dir\local_api_credentials.yaml
|
||||
$null = New-Item -ItemType File $config_dir\online_api_credentials.yaml
|
||||
#envsubst < "./config/dev.yaml" > $BASE/dev.yaml
|
||||
Copy-Item .\config\dev.yaml $base\dev.yaml
|
||||
$plugins | ForEach-Object {
|
||||
Copy-Item $plugins_dir\$notif_dir\$_\notification-$_.exe $base\$plugins_dir\notification-$_.exe
|
||||
Copy-Item $plugins_dir\$notif_dir\$_\$_.yaml $config_dir\$notif_dir\$_.yaml
|
||||
}
|
||||
}
|
||||
|
||||
function setup() {
|
||||
& $base\cscli.exe -c "$config_file" hub update
|
||||
& $base\cscli.exe -c "$config_file" collections install crowdsecurity/linux crowdsecurity/windows
|
||||
}
|
||||
|
||||
function setup_api() {
|
||||
& $base\cscli.exe -c "$config_file" machines add test -p testpassword -f $config_dir\local_api_credentials.yaml --force
|
||||
}
|
||||
|
||||
if ($help) {
|
||||
show_help
|
||||
exit 0;
|
||||
}
|
||||
|
||||
$null = New-Item -ItemType Directory $base
|
||||
|
||||
$base=(Resolve-Path $base).Path
|
||||
$data_dir="$base\data"
|
||||
$log_dir="$base\logs\"
|
||||
$config_dir="$base\config"
|
||||
$config_file="$base\dev.yaml"
|
||||
$cscli_dir="$config_dir\crowdsec-cli"
|
||||
$parser_dir="$config_dir\parsers"
|
||||
$parser_s00="$parser_dir\s00-raw"
|
||||
$parser_s01="$parser_dir\s01-parse"
|
||||
$parser_s02="$parser_dir\s02-enrich"
|
||||
$scenarios_dir="$config_dir\scenarios"
|
||||
$postoverflows_dir="$config_dir\postoverflows"
|
||||
$hub_dir="$config_dir\hub"
|
||||
$plugins=@("http", "slack", "splunk")
|
||||
$plugins_dir="plugins"
|
||||
$notif_dir="notifications"
|
||||
|
||||
|
||||
Write-Output "Creating test arbo in $base"
|
||||
create_arbo
|
||||
Write-Output "Arbo created"
|
||||
Write-Output "Copying files"
|
||||
copy_file
|
||||
Write-Output "Files copied"
|
||||
Write-Output "Setting up configuration"
|
||||
$cur_path=$pwd
|
||||
Set-Location $base
|
||||
setup_api
|
||||
setup
|
||||
Set-Location $cur_path
|
|
@ -87,7 +87,10 @@ bats-fixture:
|
|||
|
||||
# Remove the local crowdsec installation and the fixture config + data
|
||||
bats-clean:
|
||||
@$(RM) -r $(LOCAL_DIR) $(LOCAL_INIT_DIR) $(TEST_DIR)/dyn-bats/*.bats tests/.environment.sh
|
||||
@$(RM) $(LOCAL_DIR) $(WIN_IGNORE_ERR)
|
||||
@$(RM) $(LOCAL_INIT_DIR) $(WIN_IGNORE_ERR)
|
||||
@$(RM) $(TEST_DIR)/dyn-bats/*.bats $(WIN_IGNORE_ERR)
|
||||
@$(RM) tests/.environment.sh $(WIN_IGNORE_ERR)
|
||||
|
||||
# Run the test suite
|
||||
bats-test: bats-environment bats-check-requirements
|
||||
|
|
133
windows/Chocolatey/crowdsec/ReadMe.md
Normal file
133
windows/Chocolatey/crowdsec/ReadMe.md
Normal file
|
@ -0,0 +1,133 @@
|
|||
## Summary
|
||||
How do I create packages? See https://docs.chocolatey.org/en-us/create/create-packages
|
||||
|
||||
If you are submitting packages to the community feed (https://community.chocolatey.org)
|
||||
always try to ensure you have read, understood and adhere to the create
|
||||
packages wiki link above.
|
||||
|
||||
## Automatic Packaging Updates?
|
||||
Consider making this package an automatic package, for the best
|
||||
maintainability over time. Read up at https://docs.chocolatey.org/en-us/create/automatic-packages
|
||||
|
||||
## Shim Generation
|
||||
Any executables you include in the package or download (but don't call
|
||||
install against using the built-in functions) will be automatically shimmed.
|
||||
|
||||
This means those executables will automatically be included on the path.
|
||||
Shim generation runs whether the package is self-contained or uses automation
|
||||
scripts.
|
||||
|
||||
By default, these are considered console applications.
|
||||
|
||||
If the application is a GUI, you should create an empty file next to the exe
|
||||
named 'name.exe.gui' e.g. 'bob.exe' would need a file named 'bob.exe.gui'.
|
||||
See https://docs.chocolatey.org/en-us/create/create-packages#how-do-i-set-up-shims-for-applications-that-have-a-gui
|
||||
|
||||
If you want to ignore the executable, create an empty file next to the exe
|
||||
named 'name.exe.ignore' e.g. 'bob.exe' would need a file named
|
||||
'bob.exe.ignore'.
|
||||
See https://docs.chocolatey.org/en-us/create/create-packages#how-do-i-exclude-executables-from-getting-shims
|
||||
|
||||
## Self-Contained?
|
||||
If you have a self-contained package, you can remove the automation scripts
|
||||
entirely and just include the executables, they will automatically get shimmed,
|
||||
which puts them on the path. Ensure you have the legal right to distribute
|
||||
the application though. See https://docs.chocolatey.org/en-us/information/legal.
|
||||
|
||||
You should read up on the Shim Generation section to familiarize yourself
|
||||
on what to do with GUI applications and/or ignoring shims.
|
||||
|
||||
## Automation Scripts
|
||||
You have a powerful use of Chocolatey, as you are using PowerShell. So you
|
||||
can do just about anything you need. Choco has some very handy built-in
|
||||
functions that you can use, these are sometimes called the helpers.
|
||||
|
||||
### Built-In Functions
|
||||
https://docs.chocolatey.org/en-us/create/functions
|
||||
|
||||
A note about a couple:
|
||||
* Get-BinRoot - this is a horribly named function that doesn't do what new folks think it does. It gets you the 'tools' root, which by default is set to 'c:\tools', not the chocolateyInstall bin folder - see https://docs.chocolatey.org/en-us/create/functions/get-toolslocation
|
||||
* Install-BinFile - used for non-exe files - executables are automatically shimmed... - see https://docs.chocolatey.org/en-us/create/functions/install-binfile
|
||||
* Uninstall-BinFile - used for non-exe files - executables are automatically shimmed - see https://docs.chocolatey.org/en-us/create/functions/uninstall-binfile
|
||||
|
||||
### Getting package specific information
|
||||
Use the package parameters pattern - see https://docs.chocolatey.org/en-us/guides/create/parse-packageparameters-argument
|
||||
|
||||
### Need to mount an ISO?
|
||||
https://docs.chocolatey.org/en-us/guides/create/mount-an-iso-in-chocolatey-package
|
||||
|
||||
### Environment Variables
|
||||
Chocolatey makes a number of environment variables available (You can access any of these with $env:TheVariableNameBelow):
|
||||
|
||||
* TEMP/TMP - Overridden to the CacheLocation, but may be the same as the original TEMP folder
|
||||
* ChocolateyInstall - Top level folder where Chocolatey is installed
|
||||
* ChocolateyPackageName - The name of the package, equivalent to the `<id />` field in the nuspec (0.9.9+)
|
||||
* ChocolateyPackageTitle - The title of the package, equivalent to the `<title />` field in the nuspec (0.10.1+)
|
||||
* ChocolateyPackageVersion - The version of the package, equivalent to the `<version />` field in the nuspec (0.9.9+)
|
||||
* ChocolateyPackageFolder - The top level location of the package folder - the folder where Chocolatey has downloaded and extracted the NuGet package, typically `C:\ProgramData\chocolatey\lib\packageName`.
|
||||
|
||||
#### Advanced Environment Variables
|
||||
The following are more advanced settings:
|
||||
|
||||
* ChocolateyPackageParameters - Parameters to use with packaging, not the same as install arguments (which are passed directly to the native installer). Based on `--package-parameters`. (0.9.8.22+)
|
||||
* CHOCOLATEY_VERSION - The version of Choco you normally see. Use if you are 'lighting' things up based on choco version. (0.9.9+) - Otherwise take a dependency on the specific version you need.
|
||||
* ChocolateyForceX86 = If available and set to 'true', then user has requested 32bit version. (0.9.9+) - Automatically handled in built in Choco functions.
|
||||
* OS_PLATFORM - Like Windows, OSX, Linux. (0.9.9+)
|
||||
* OS_VERSION - The version of OS, like 6.1 something something for Windows. (0.9.9+)
|
||||
* OS_NAME - The reported name of the OS. (0.9.9+)
|
||||
* USER_NAME = The user name (0.10.6+)
|
||||
* USER_DOMAIN = The user domain name (could also be local computer name) (0.10.6+)
|
||||
* IS_PROCESSELEVATED = Is the process elevated? (0.9.9+)
|
||||
* IS_SYSTEM = Is the user the system account? (0.10.6+)
|
||||
* IS_REMOTEDESKTOP = Is the user in a terminal services session? (0.10.6+)
|
||||
* ChocolateyToolsLocation - formerly 'ChocolateyBinRoot' ('ChocolateyBinRoot' will be removed with Chocolatey v2.0.0), this is where tools being installed outside of Chocolatey packaging will go. (0.9.10+)
|
||||
|
||||
#### Set By Options and Configuration
|
||||
Some environment variables are set based on options that are passed, configuration and/or features that are turned on:
|
||||
|
||||
* ChocolateyEnvironmentDebug - Was `--debug` passed? If using the built-in PowerShell host, this is always true (but only logs debug messages to console if `--debug` was passed) (0.9.10+)
|
||||
* ChocolateyEnvironmentVerbose - Was `--verbose` passed? If using the built-in PowerShell host, this is always true (but only logs verbose messages to console if `--verbose` was passed). (0.9.10+)
|
||||
* ChocolateyExitOnRebootDetected - Are we exiting on a detected reboot? Set by ` --exit-when-reboot-detected` or the feature `exitOnRebootDetected` (0.11.0+)
|
||||
* ChocolateyForce - Was `--force` passed? (0.9.10+)
|
||||
* ChocolateyForceX86 - Was `-x86` passed? (CHECK)
|
||||
* ChocolateyRequestTimeout - How long before a web request will time out. Set by config `webRequestTimeoutSeconds` (CHECK)
|
||||
* ChocolateyResponseTimeout - How long to wait for a download to complete? Set by config `commandExecutionTimeoutSeconds` (CHECK)
|
||||
* ChocolateyPowerShellHost - Are we using the built-in PowerShell host? Set by `--use-system-powershell` or the feature `powershellHost` (0.9.10+)
|
||||
|
||||
#### Business Edition Variables
|
||||
|
||||
* ChocolateyInstallArgumentsSensitive - Encrypted arguments passed from command line `--install-arguments-sensitive` that are not logged anywhere. (0.10.1+ and licensed editions 1.6.0+)
|
||||
* ChocolateyPackageParametersSensitive - Package parameters passed from command line `--package-parameters-senstivite` that are not logged anywhere. (0.10.1+ and licensed editions 1.6.0+)
|
||||
* ChocolateyLicensedVersion - What version is the licensed edition on?
|
||||
* ChocolateyLicenseType - What edition / type of the licensed edition is installed?
|
||||
* USER_CONTEXT - The original user context - different when self-service is used (Licensed v1.10.0+)
|
||||
|
||||
#### Experimental Environment Variables
|
||||
The following are experimental or use not recommended:
|
||||
|
||||
* OS_IS64BIT = This may not return correctly - it may depend on the process the app is running under (0.9.9+)
|
||||
* CHOCOLATEY_VERSION_PRODUCT = the version of Choco that may match CHOCOLATEY_VERSION but may be different (0.9.9+) - based on git describe
|
||||
* IS_ADMIN = Is the user an administrator? But doesn't tell you if the process is elevated. (0.9.9+)
|
||||
* IS_REMOTE = Is the user in a remote session? (0.10.6+)
|
||||
|
||||
#### Not Useful Or Anti-Pattern If Used
|
||||
|
||||
* ChocolateyInstallOverride = Not for use in package automation scripts. Based on `--override-arguments` being passed. (0.9.9+)
|
||||
* ChocolateyInstallArguments = The installer arguments meant for the native installer. You should use chocolateyPackageParameters instead. Based on `--install-arguments` being passed. (0.9.9+)
|
||||
* ChocolateyIgnoreChecksums - Was `--ignore-checksums` passed or the feature `checksumFiles` turned off? (0.9.9.9+)
|
||||
* ChocolateyAllowEmptyChecksums - Was `--allow-empty-checksums` passed or the feature `allowEmptyChecksums` turned on? (0.10.0+)
|
||||
* ChocolateyAllowEmptyChecksumsSecure - Was `--allow-empty-checksums-secure` passed or the feature `allowEmptyChecksumsSecure` turned on? (0.10.0+)
|
||||
* ChocolateyChecksum32 - Was `--download-checksum` passed? (0.10.0+)
|
||||
* ChocolateyChecksumType32 - Was `--download-checksum-type` passed? (0.10.0+)
|
||||
* ChocolateyChecksum64 - Was `--download-checksum-x64` passed? (0.10.0)+
|
||||
* ChocolateyChecksumType64 - Was `--download-checksum-type-x64` passed? (0.10.0)+
|
||||
* ChocolateyPackageExitCode - The exit code of the script that just ran - usually set by `Set-PowerShellExitCode` (CHECK)
|
||||
* ChocolateyLastPathUpdate - Set by Chocolatey as part of install, but not used for anything in particular in packaging.
|
||||
* ChocolateyProxyLocation - The explicit proxy location as set in the configuration `proxy` (0.9.9.9+)
|
||||
* ChocolateyDownloadCache - Use available download cache? Set by `--skip-download-cache`, `--use-download-cache`, or feature `downloadCache` (0.9.10+ and licensed editions 1.1.0+)
|
||||
* ChocolateyProxyBypassList - Explicitly set locations to ignore in configuration `proxyBypassList` (0.10.4+)
|
||||
* ChocolateyProxyBypassOnLocal - Should the proxy bypass on local connections? Set based on configuration `proxyBypassOnLocal` (0.10.4+)
|
||||
* http_proxy - Set by original `http_proxy` passthrough, or same as `ChocolateyProxyLocation` if explicitly set. (0.10.4+)
|
||||
* https_proxy - Set by original `https_proxy` passthrough, or same as `ChocolateyProxyLocation` if explicitly set. (0.10.4+)
|
||||
* no_proxy- Set by original `no_proxy` passthrough, or same as `ChocolateyProxyBypassList` if explicitly set. (0.10.4+)
|
||||
|
43
windows/Chocolatey/crowdsec/crowdsec.nuspec
Normal file
43
windows/Chocolatey/crowdsec/crowdsec.nuspec
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Do not remove this test for UTF-8: if “Ω” doesn’t appear as greek uppercase omega letter enclosed in quotation marks, you should use an editor that supports UTF-8, not this one. -->
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>crowdsec</id>
|
||||
<version>1.3.3</version>
|
||||
<packageSourceUrl>https://github.com/crowdsecurity/crowdsec</packageSourceUrl>
|
||||
<owners>CrowdSecurity</owners>
|
||||
<!-- ============================== -->
|
||||
|
||||
<!-- == SOFTWARE SPECIFIC SECTION == -->
|
||||
<title>CrowdSec</title>
|
||||
<authors>CrowdSecurity</authors>
|
||||
<projectUrl>https://crowdsec.net/</projectUrl>
|
||||
<copyright>CrowdSec, 2022</copyright>
|
||||
<licenseUrl>https://github.com/crowdsecurity/crowdsec/blob/main/LICENSE</licenseUrl>
|
||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||
<projectSourceUrl>https://github.com/crowdsecurity/crowdsec</projectSourceUrl>
|
||||
<docsUrl>https://docs.crowdsec.net</docsUrl>
|
||||
<bugTrackerUrl>https://github.com/crowdsecurity/crowdsec/issues</bugTrackerUrl>
|
||||
<tags>crowdsec crowdsecurity security ips ids</tags>
|
||||
<summary>CrowdSec IDS</summary>
|
||||
<description>
|
||||
CrowdSec is a free, modern and collaborative behavior detection engine, coupled with a global IP reputation network.
|
||||
It stacks on fail2ban's philosophy but is IPV6 compatible and 60x faster (Go vs Python), uses Grok patterns to parse logs and YAML scenario to identify behaviors.
|
||||
CrowdSec is engineered for modern Cloud / Containers / VM based infrastructures (by decoupling detection and remediation). Once detected you can remedy threats with various bouncers (firewall block, nginx http 403, Captchas, etc.) while the aggressive IP can be sent to CrowdSec for curation before being shared among all users to further improve everyone's security.
|
||||
|
||||
### Package Specific
|
||||
#### Package parameters
|
||||
|
||||
- AgentOnly: If set, the local API will be disabled. You will need to register the agent in LAPI yourself and configure the service to start on boot.
|
||||
|
||||
</description>
|
||||
<!-- =============================== -->
|
||||
|
||||
<dependencies>
|
||||
<dependency id="chocolatey-core.extension" version="1.1.0" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="tools\**" target="tools" />
|
||||
</files>
|
||||
</package>
|
26
windows/Chocolatey/crowdsec/tools/LICENSE.txt
Normal file
26
windows/Chocolatey/crowdsec/tools/LICENSE.txt
Normal file
|
@ -0,0 +1,26 @@
|
|||
|
||||
From: https://github.com/crowdsecurity/crowdsec/blob/master/LICENSE
|
||||
|
||||
LICENSE
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 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.
|
9
windows/Chocolatey/crowdsec/tools/VERIFICATION.txt
Normal file
9
windows/Chocolatey/crowdsec/tools/VERIFICATION.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
|
||||
VERIFICATION
|
||||
Verification is intended to assist the Chocolatey moderators and community
|
||||
in verifying that this package's contents are trustworthy.
|
||||
|
||||
This package is published by CrowdSecurity itself. The MSI is identical to the one published in the github releases for the project.
|
||||
You can download the MSI from the latest release or pre-release here: https://github.com/crowdsecurity/crowdsec/releases
|
||||
The MSI is also digitally signed.
|
|
@ -0,0 +1 @@
|
|||
Stop-Service crowdsec
|
29
windows/Chocolatey/crowdsec/tools/chocolateyinstall.ps1
Normal file
29
windows/Chocolatey/crowdsec/tools/chocolateyinstall.ps1
Normal file
|
@ -0,0 +1,29 @@
|
|||
$ErrorActionPreference = 'Stop';
|
||||
$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
|
||||
$fileLocation = Join-Path $toolsDir 'crowdsec.msi'
|
||||
|
||||
$silentArgs = "/qn /norestart /l*v `"$($env:TEMP)\$($packageName).$($env:chocolateyPackageVersion).MsiInstall.log`""
|
||||
|
||||
|
||||
$pp = Get-PackageParameters
|
||||
|
||||
if ($pp['AgentOnly']) {
|
||||
$silentArgs += " AGENT_ONLY=1"
|
||||
}
|
||||
|
||||
|
||||
$packageArgs = @{
|
||||
packageName = $env:ChocolateyPackageName
|
||||
unzipLocation = $toolsDir
|
||||
fileType = 'msi'
|
||||
file64 = $fileLocation
|
||||
softwareName = 'Crowdsec'
|
||||
silentArgs = $silentArgs
|
||||
validExitCodes= @(0, 3010, 1641)
|
||||
}
|
||||
|
||||
Install-ChocolateyInstallPackage @packageArgs
|
||||
|
||||
if ($pp['AgentOnly']) {
|
||||
Write-Host "/AgentOnly was specified. LAPI is disabled, please register your agent manually and configure the service to start on boot."
|
||||
}
|
30
windows/Chocolatey/crowdsec/tools/chocolateyuninstall.ps1
Normal file
30
windows/Chocolatey/crowdsec/tools/chocolateyuninstall.ps1
Normal file
|
@ -0,0 +1,30 @@
|
|||
$ErrorActionPreference = 'Stop';
|
||||
$packageArgs = @{
|
||||
packageName = $env:ChocolateyPackageName
|
||||
softwareName = 'Crowdsec'
|
||||
fileType = 'MSI'
|
||||
silentArgs = "/qn /norestart"
|
||||
validExitCodes= @(0, 3010, 1605, 1614, 1641)
|
||||
}
|
||||
|
||||
[array]$key = Get-UninstallRegistryKey -SoftwareName $packageArgs['softwareName']
|
||||
|
||||
if ($key.Count -eq 1) {
|
||||
$key | % {
|
||||
$packageArgs['file'] = "$($_.UninstallString)"
|
||||
if ($packageArgs['fileType'] -eq 'MSI') {
|
||||
$packageArgs['silentArgs'] = "$($_.PSChildName) $($packageArgs['silentArgs'])"
|
||||
$packageArgs['file'] = ''
|
||||
} else {
|
||||
}
|
||||
|
||||
Uninstall-ChocolateyPackage @packageArgs
|
||||
}
|
||||
} elseif ($key.Count -eq 0) {
|
||||
Write-Warning "$packageName has already been uninstalled by other means."
|
||||
} elseif ($key.Count -gt 1) {
|
||||
Write-Warning "$($key.Count) matches found!"
|
||||
Write-Warning "To prevent accidental data loss, no programs will be uninstalled."
|
||||
Write-Warning "Please alert package maintainer the following keys were matched:"
|
||||
$key | % {Write-Warning "- $($_.DisplayName)"}
|
||||
}
|
7
windows/install_dev_windows.ps1
Normal file
7
windows/install_dev_windows.ps1
Normal file
|
@ -0,0 +1,7 @@
|
|||
#install choco
|
||||
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
|
||||
choco install -y golang
|
||||
choco install -y jq
|
||||
choco install -y git
|
||||
choco install -y mingw
|
||||
refreshenv
|
2
windows/install_installer_windows.ps1
Normal file
2
windows/install_installer_windows.ps1
Normal file
|
@ -0,0 +1,2 @@
|
|||
choco install -y wixtoolset
|
||||
$env:Path += ";C:\Program Files (x86)\WiX Toolset v3.11\bin"
|
65
windows/installer/WixUI_HK.wxs
Normal file
65
windows/installer/WixUI_HK.wxs
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<Fragment>
|
||||
|
||||
<UI Id="WixUI_HK">
|
||||
<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
|
||||
<TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
|
||||
<TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />
|
||||
|
||||
<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
|
||||
<Property Id="WixUI_Mode" Value="InstallDir" />
|
||||
|
||||
<DialogRef Id="BrowseDlg" />
|
||||
<DialogRef Id="DiskCostDlg" />
|
||||
<DialogRef Id="ErrorDlg" />
|
||||
<DialogRef Id="FatalError" />
|
||||
<DialogRef Id="FilesInUse" />
|
||||
<DialogRef Id="MsiRMFilesInUse" />
|
||||
<DialogRef Id="PrepareDlg" />
|
||||
<DialogRef Id="ProgressDlg" />
|
||||
<DialogRef Id="ResumeDlg" />
|
||||
<DialogRef Id="UserExit" />
|
||||
|
||||
|
||||
<Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath" Order="3">1</Publish>
|
||||
<Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4"><![CDATA[WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>
|
||||
|
||||
<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999">1</Publish>
|
||||
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg">NOT Installed</Publish>
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">Installed AND PATCH</Publish>
|
||||
|
||||
<Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg">1</Publish>
|
||||
<Publish Dialog="InstallDirDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
|
||||
<Publish Dialog="InstallDirDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath" Order="2">NOT WIXUI_DONTVALIDATEPATH</Publish>
|
||||
<Publish Dialog="InstallDirDlg" Control="Next" Event="SpawnDialog" Value="InvalidDirDlg" Order="3"><![CDATA[NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>
|
||||
<Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="4">WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1"</Publish>
|
||||
|
||||
<Publish Dialog="InstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
|
||||
<Publish Dialog="InstallDirDlg" Control="ChangeFolder" Event="SpawnDialog" Value="BrowseDlg" Order="2">1</Publish>
|
||||
|
||||
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2">Installed</Publish>
|
||||
|
||||
<Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg">1</Publish>
|
||||
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg">1</Publish>
|
||||
|
||||
<ProgressText Action="HubUpdate">Updating Hub content</ProgressText>
|
||||
<ProgressText Action="InstallWinCollection">Installing Windows collection</ProgressText>
|
||||
<ProgressText Action="RegisterMachine">Registering agent to local API</ProgressText>
|
||||
<ProgressText Action="RegisterCAPI">Registering to Crowdsec central API</ProgressText>
|
||||
|
||||
|
||||
|
||||
</UI>
|
||||
|
||||
<UIRef Id="WixUI_Common" />
|
||||
<WixVariable Id="WixUIDialogBmp" Value="..\windows\installer\installer_dialog.bmp" />
|
||||
<WixVariable Id="WixUIBannerBmp" Value="..\windows\installer\crowdsec_msi_top_banner.bmp" />
|
||||
<Icon Id="icon.ico" SourceFile="..\windows\installer\crowdsec_icon.ico"/>
|
||||
<Property Id="ARPPRODUCTICON" Value="icon.ico" />
|
||||
</Fragment>
|
||||
</Wix>
|
BIN
windows/installer/crowdsec_icon.ico
Normal file
BIN
windows/installer/crowdsec_icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
BIN
windows/installer/crowdsec_msi_top_banner.bmp
Normal file
BIN
windows/installer/crowdsec_msi_top_banner.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 112 KiB |
BIN
windows/installer/installer_dialog.bmp
Normal file
BIN
windows/installer/installer_dialog.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 152 KiB |
165
windows/installer/product.wxs
Normal file
165
windows/installer/product.wxs
Normal file
|
@ -0,0 +1,165 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<?if $(sys.BUILDARCH)="x86"?>
|
||||
<?define Program_Files="ProgramFilesFolder"?>
|
||||
<?elseif $(sys.BUILDARCH)="x64"?>
|
||||
<?define Program_Files="ProgramFiles64Folder"?>
|
||||
<?else ?>
|
||||
<?error Unsupported value of sys.BUILDARCH=$(sys.BUILDARCH)?>
|
||||
<?endif ?>
|
||||
|
||||
<?define ProductName="CrowdSec"?>
|
||||
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
|
||||
<Product Id="*" UpgradeCode="8eab6970-25e3-4b7d-882f-5b7efa311afc" Name="$(var.ProductName)" Version="$(var.Version)" Manufacturer="CrowdSecurity" Language="1033">
|
||||
|
||||
<Package InstallerVersion="200" Compressed="yes" Comments="Crowdsec Installer Package" InstallScope="perMachine" />
|
||||
<Media Id="1" Cabinet="product.cab" EmbedCab="yes" />
|
||||
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." Schedule="afterInstallExecute" />
|
||||
|
||||
<SetProperty After="AppSearch" Id="WIXUI_EXITDIALOGOPTIONALTEXT" Value="LAPI is disabled and no agents credentials were generated.Please register your agent in your LAPI instance, update C:\ProgramData\CrowdSec\config\local_api_credentials.yaml, and configure the CrowdSec service to start on boot." >
|
||||
AGENT_ONLY
|
||||
</SetProperty>
|
||||
|
||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||
<Directory Id="$(var.Program_Files)">
|
||||
<Directory Id="INSTALLDIR" Name="$(var.ProductName)">
|
||||
<Component Id="Crowdsec" Guid="200299fc-e728-4749-a283-cfbd20c02a59">
|
||||
<File Id="crowdsec.exe" Source="cmd\crowdsec\crowdsec.exe" />
|
||||
<Condition>NOT AGENT_ONLY</Condition>
|
||||
<ServiceInstall Id="CrowdsecService" Name="Crowdsec" DisplayName="Crowdsec" Description="Crowdsec IPS/IDS" Start="auto" Type="ownProcess" ErrorControl="normal" Account="LocalSystem" Vital="yes" Interactive="no" />
|
||||
<ServiceControl Id="CrowdsecService" Name="Crowdsec" Start="install" Stop="both" Remove="uninstall" Wait="yes" />
|
||||
</Component>
|
||||
<Component Id="CrowdsecNoStart" Guid="a9fac892-a7ea-4a3b-9a00-4f4bf2205de9">
|
||||
<File Id="crowdsec2.exe" Source="cmd\crowdsec\crowdsec.exe" />
|
||||
<Condition>AGENT_ONLY</Condition>
|
||||
<ServiceInstall Id="CrowdsecService2" Name="Crowdsec" DisplayName="Crowdsec" Description="Crowdsec IPS/IDS" Start="disabled" Type="ownProcess" ErrorControl="normal" Account="LocalSystem" Vital="yes" Interactive="no" />
|
||||
<ServiceControl Id="CrowdsecService2" Name="Crowdsec" Stop="both" Remove="uninstall" Wait="yes" />
|
||||
</Component>
|
||||
<Component Id="Cscli" Guid="b3da82cb-d111-4205-b0ee-5499b34dd57b">
|
||||
<File Id="cscli.exe" Source="cmd\crowdsec-cli\cscli.exe" />
|
||||
<Environment Id="UpdatePath" Name="PATH" Value="[INSTALLDIR]" Part="last" Action="set" System="yes" />
|
||||
</Component>
|
||||
</Directory>
|
||||
</Directory>
|
||||
<Directory Id="CommonAppDataFolder">
|
||||
<Directory Id="CrowdSecCommonDir" Name="CrowdSec">
|
||||
<Directory Id="ConfigDir" Name="config">
|
||||
<Component Id="AcquisConfig" Guid="08bfc6fd-4811-48ed-b63e-035acb1f69d8">
|
||||
<File Id="acquis.yaml" Source="config\acquis_win.yaml" Name="acquis.yaml" />
|
||||
</Component>
|
||||
<Component Id="LocalCreds" Guid="fea92471-ba4b-4067-a92a-19af0d581b60">
|
||||
<File Id="local_api_credentials.yaml" Source="config\local_api_credentials.yaml" />
|
||||
</Component>
|
||||
<Component Id="OnlineCreds" Guid="a652a6cb-d464-40b1-8f50-78dce0135d20">
|
||||
<File Id="online_api_credentials.yaml" Source="config\online_api_credentials.yaml" />
|
||||
</Component>
|
||||
<Component Id="ProfilesConfig" Guid="8d6fca04-b3be-4a52-a9df-278139d0498e">
|
||||
<File Id="profiles.yaml" Source="config\profiles.yaml" />
|
||||
</Component>
|
||||
<Component Id="SimulationConfig" Guid="a27346e6-af4a-4ee6-aea9-d783b036cd21">
|
||||
<File Id="simulation.yaml" Source="config\simulation.yaml" />
|
||||
</Component>
|
||||
<Component Id="ConsoleConfig" Guid="8393e488-18d5-4578-9e4c-99b54f7b2bb6">
|
||||
<File Id="console.yaml" Source="config\console.yaml" />
|
||||
</Component>
|
||||
<Component Id="Csconfig_lapi" Guid="a99bd70c-61af-43ca-8394-6dc789cec566">
|
||||
<Condition>
|
||||
NOT AGENT_ONLY
|
||||
</Condition>
|
||||
<File Id="config.yaml" Source="config\config_win.yaml" Name="config.yaml"/>
|
||||
</Component>
|
||||
<Component Id="Csconfig_no_lapi" Guid="494d2e56-9db0-4d31-bde4-826f28a5683c">
|
||||
<Condition>
|
||||
AGENT_ONLY
|
||||
</Condition>
|
||||
<File Id="config_no_lapi.yaml" Source="config\config_win_no_lapi.yaml" Name="config.yaml"/>
|
||||
</Component>
|
||||
<Directory Id="NotifConfigDir" Name="notifications">
|
||||
<Component Id="NotifConfig" Guid="4d04a852-e876-408f-95a7-a7effa7762c4">
|
||||
<File Id="slack.yaml" Source="plugins\notifications\slack\slack.yaml" Name="slack.yaml" />
|
||||
<File Id="http.yaml" Source="plugins\notifications\http\http.yaml" Name="http.yaml" />
|
||||
<File Id="email.yaml" Source="plugins\notifications\email\email.yaml" Name="email.yaml" />
|
||||
<File Id="splunk.yaml" Source="plugins\notifications\splunk\splunk.yaml" Name="splunk.yaml" />
|
||||
</Component>
|
||||
</Directory>
|
||||
<Directory Id="PatternsDir" Name="patterns" />
|
||||
</Directory>
|
||||
<Directory Id="logCrowdsec" Name="log">
|
||||
<Component Id="CreateLog" Guid="bfb37d14-10c4-40fb-bafa-2a29f95e4a53">
|
||||
<CreateFolder />
|
||||
</Component>
|
||||
</Directory>
|
||||
<Directory Id="hubCrowdsec" Name="hub">
|
||||
<Component Id="CreateHub" Guid="ac528dd2-49f7-4448-a9e7-91c66061404b">
|
||||
<CreateFolder />
|
||||
</Component>
|
||||
</Directory>
|
||||
<Directory Id="CrowdsecDataDir" Name="data">
|
||||
<Component Id="CreateCrowdsecDataDir" Guid="de529565-a499-4327-948d-2a318f8e822a">
|
||||
<CreateFolder />
|
||||
</Component>
|
||||
</Directory>
|
||||
<Directory Id="CrowdsecPluginsDir" Name="plugins">
|
||||
<Component Id="CreateCrowdsecPluginsDir" Guid="bb7c8f19-8457-44b9-a538-aed494ec575d">
|
||||
<File Id="notification_slack.exe" Source="plugins\notifications\slack\notification-slack.exe" />
|
||||
<File Id="notification_email.exe" Source="plugins\notifications\email\notification-email.exe" />
|
||||
<File Id="notification_http.exe" Source="plugins\notifications\http\notification-http.exe" />
|
||||
<File Id="notification_splunk.exe" Source="plugins\notifications\splunk\notification-splunk.exe" />
|
||||
</Component>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Directory>
|
||||
|
||||
|
||||
<SetProperty Id="HubUpdate" Value=""[INSTALLDIR]\cscli.exe" hub update" Sequence="execute" Before="HubUpdate" />
|
||||
<CustomAction Id="HubUpdate" BinaryKey="WixCA" DllEntry="WixQuietExec" Execute="deferred" Return="check" Impersonate="no" />
|
||||
<SetProperty Id="InstallWinCollection" Value=""[INSTALLDIR]\cscli.exe" collections install crowdsecurity/windows" Sequence="execute" Before="InstallWinCollection" />
|
||||
<CustomAction Id="InstallWinCollection" BinaryKey="WixCA" DllEntry="WixQuietExec" Execute="deferred" Return="check" Impersonate="no" />
|
||||
<SetProperty Id="RegisterMachine" Value=""[INSTALLDIR]\cscli.exe" machines add -a" Sequence="execute" Before="RegisterMachine" />
|
||||
<CustomAction Id="RegisterMachine" BinaryKey="WixCA" DllEntry="WixQuietExec" Execute="deferred" Return="check" Impersonate="no" />
|
||||
<SetProperty Id="RegisterCAPI" Value=""[INSTALLDIR]\cscli.exe" capi register" Sequence="execute" Before="RegisterMachine" />
|
||||
<CustomAction Id="RegisterCAPI" BinaryKey="WixCA" DllEntry="WixQuietExec" Execute="deferred" Return="check" Impersonate="no" />
|
||||
<InstallExecuteSequence>
|
||||
<WriteEnvironmentStrings />
|
||||
<Custom Action="HubUpdate" After="InstallFiles">NOT Installed AND NOT REMOVE</Custom>
|
||||
<Custom Action="InstallWinCollection" After="HubUpdate">NOT Installed AND NOT REMOVE</Custom>
|
||||
<Custom Action="RegisterMachine" After="InstallWinCollection">NOT Installed AND NOT REMOVE AND NOT AGENT_ONLY AND NOT WIX_UPGRADE_DETECTED</Custom>
|
||||
<Custom Action="RegisterCAPI" After="RegisterMachine">NOT Installed AND NOT REMOVE AND NOT AGENT_ONLY AND NOT WIX_UPGRADE_DETECTED</Custom>
|
||||
</InstallExecuteSequence>
|
||||
|
||||
<Feature Id="DefaultFeature" Level="1">
|
||||
<ComponentRef Id="Crowdsec" />
|
||||
<ComponentRef Id="CrowdsecNoStart" />
|
||||
<ComponentRef Id="Cscli" />
|
||||
<ComponentRef Id="AcquisConfig"/>
|
||||
<ComponentRef Id="LocalCreds"/>
|
||||
<ComponentRef Id="OnlineCreds"/>
|
||||
<ComponentRef Id="ProfilesConfig"/>
|
||||
<ComponentRef Id="SimulationConfig"/>
|
||||
<ComponentRef Id="ConsoleConfig"/>
|
||||
<ComponentRef Id="CreateLog" />
|
||||
<ComponentRef Id="CreateHub" />
|
||||
<ComponentRef Id="NotifConfig" />
|
||||
<ComponentRef Id="CreateCrowdsecPluginsDir"/>
|
||||
<ComponentRef Id="CreateCrowdsecDataDir" />
|
||||
<ComponentRef Id="Csconfig_lapi" />
|
||||
<ComponentRef Id="Csconfig_no_lapi" />
|
||||
<ComponentGroupRef Id="CrowdsecPatterns" />
|
||||
</Feature>
|
||||
|
||||
<UI>
|
||||
<UIRef Id="WixUI_HK" />
|
||||
</UI>
|
||||
|
||||
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLDIR" />
|
||||
<Property Id="MsiLogging" Value="voicewarmupx!" />
|
||||
|
||||
<!-- this should help to propagate env var changes -->
|
||||
<CustomActionRef Id="WixBroadcastEnvironmentChange" />
|
||||
|
||||
</Product>
|
||||
|
||||
</Wix>
|
32
windows/windows.md
Normal file
32
windows/windows.md
Normal file
|
@ -0,0 +1,32 @@
|
|||
**POC Windows version of crowdsec**
|
||||
|
||||
To test and develop on windows first execute the script that will install all required tools for windows [install dev on windows](/windows/install_dev_windows.ps1)
|
||||
copy the script locally open a powershell window or launch powershell from command line
|
||||
powershell
|
||||
./install_dev_windows.ps1
|
||||
when all the required packages are installed
|
||||
Clone the project and build manually the client and the cli
|
||||
in cmd/crowdsec and cmd/crowdsec-cli with go build
|
||||
you should now have a crowdsec.exe and crowdsec-cli.exe
|
||||
|
||||
To make the installer and package first install the packages required executing the script
|
||||
[install installer on windows](/windows/install_installer_windows.ps1)
|
||||
|
||||
And finally to create the choco package and msi execute the script at root level
|
||||
[make installer](/install_installer_windows.ps1)
|
||||
./make_installer.ps1
|
||||
|
||||
You should now have a CrowdSec.0.0.1.nupkg file
|
||||
you can test it using
|
||||
choco install CrowdSec.0.0.1.nupkg
|
||||
it will install and configure crowdsec for windows.
|
||||
|
||||
To test it navigate to C:\Program Files\CrowdSec and test the cli
|
||||
.\crowdsec-cli.exe metrics
|
||||
|
||||
Install something from the hub
|
||||
.\crowdsec-cli.exe parsers install crowdsecurity/syslog-logs
|
||||
|
||||
and restart the windows service
|
||||
net start crowdsec
|
||||
net stop crowdsec
|
Loading…
Reference in a new issue