pax_global_header 0000666 0000000 0000000 00000000064 14350614034 0014512 g ustar 00root root 0000000 0000000 52 comment=0a4fde8e9440927d02ce187d1716306af9a13780 cs-firewall-bouncer-0.0.25/ 0000775 0000000 0000000 00000000000 14350614034 0015441 5 ustar 00root root 0000000 0000000 cs-firewall-bouncer-0.0.25/.github/ 0000775 0000000 0000000 00000000000 14350614034 0017001 5 ustar 00root root 0000000 0000000 cs-firewall-bouncer-0.0.25/.github/release-drafter.yml 0000664 0000000 0000000 00000000056 14350614034 0022572 0 ustar 00root root 0000000 0000000 template: | ## What’s Changed $CHANGES cs-firewall-bouncer-0.0.25/.github/workflows/ 0000775 0000000 0000000 00000000000 14350614034 0021036 5 ustar 00root root 0000000 0000000 cs-firewall-bouncer-0.0.25/.github/workflows/build-binary-package.yml 0000664 0000000 0000000 00000004071 14350614034 0025535 0 ustar 00root root 0000000 0000000 # .github/workflows/build-docker-image.yml name: build-binary-package on: release: types: prereleased jobs: build-binary-package: name: Build and upload binary package runs-on: ubuntu-latest strategy: matrix: static: [false, true] target: - goarch: 386 arch: i386 goarm: "" - goarch: arm64 arch: aarch64 goarm: "" - goarch: arm arch: armv7 goarm: "7" - goarch: arm arch: armhf goarm: "6" - goarch: amd64 arch: amd64 goarm: "" steps: - name: Set up Go 1.19 uses: actions/setup-go@v3 with: go-version: 1.19 id: go - name: Check out code into the Go module directory uses: actions/checkout@v3 with: fetch-depth: 0 - name: Build ${{ matrix.target.arch }} binaries env: GOARCH: ${{ matrix.target.goarch }} GOARM: ${{ matrix.target.goarm }} run: | make release mv crowdsec-firewall-bouncer.tgz crowdsec-firewall-bouncer-${{ matrix.target.arch }}.tgz if: ${{ !matrix.static }} - name: Build ${{ matrix.target.arch }} binaries (static) env: GOARCH: ${{ matrix.target.goarch }} GOARM: ${{ matrix.target.goarm }} run: | make release BUILD_STATIC=yes mv crowdsec-firewall-bouncer.tgz crowdsec-firewall-bouncer-${{ matrix.target.arch }}-static.tgz if: ${{ matrix.static }} - name: Upload to release uses: JasonEtco/upload-to-release@master with: args: crowdsec-firewall-bouncer-${{ matrix.target.arch }}.tgz application/x-gzip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} if: ${{ !matrix.static }} - name: Upload to release (static) uses: JasonEtco/upload-to-release@master with: args: crowdsec-firewall-bouncer-${{ matrix.target.arch }}-static.tgz application/x-gzip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} if: ${{ matrix.static }} cs-firewall-bouncer-0.0.25/.github/workflows/ci_golangci-lint.yml 0000664 0000000 0000000 00000001112 14350614034 0024756 0 ustar 00root root 0000000 0000000 name: golangci-lint on: push: tags: - v* branches: - main paths-ignore: - 'docs/**' - 'mkdocs.yml' - 'README.md' pull_request: paths-ignore: - 'docs/**' - 'mkdocs.yml' - 'README.md' jobs: golangci: name: lint runs-on: ubuntu-latest steps: - uses: actions/setup-go@v3 - uses: actions/checkout@v3 - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: version: v1.49 args: --issues-exit-code=1 --timeout 5m only-new-issues: false cs-firewall-bouncer-0.0.25/.github/workflows/codeql-analysis.yml 0000664 0000000 0000000 00000004077 14350614034 0024661 0 ustar 00root root 0000000 0000000 name: "Code Scanning - Action" on: push: branches: [main, testing] pull_request: branches: [main, testing] schedule: # ┌───────────── minute (0 - 59) # │ ┌───────────── hour (0 - 23) # │ │ ┌───────────── day of the month (1 - 31) # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT) # │ │ │ │ │ # │ │ │ │ │ # │ │ │ │ │ # * * * * * - cron: '30 1 * * 0' jobs: CodeQL-Build: # CodeQL runs on ubuntu-latest, windows-latest, and macos-latest runs-on: ubuntu-latest permissions: # required for all workflows security-events: write # only required for workflows in private repositories actions: read contents: read steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 # Override language selection by uncommenting this and choosing your languages # with: # languages: go, javascript, csharp, python, cpp, java # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below). - name: Autobuild uses: github/codeql-action/autobuild@v1 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # ✏️ If the Autobuild fails above, remove it and uncomment the following # three lines and modify them (or add more) to build your code if your # project uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 cs-firewall-bouncer-0.0.25/.github/workflows/functional_tests.yaml 0000664 0000000 0000000 00000001010 14350614034 0025276 0 ustar 00root root 0000000 0000000 name: Functional Tests on: push: branches: [ main ] pull_request: branches: [ main ] jobs: install_crowdsec: name: Build runs-on: ubuntu-latest steps: - name: Set up Go 1.19 uses: actions/setup-go@v1 with: go-version: 1.19 id: go - name: Check out code into the Go module directory uses: actions/checkout@v2 - name: Install deps for tests run: sudo apt install -y nftables iptables ipset - name: Run tests run: sudo make func-tests cs-firewall-bouncer-0.0.25/.github/workflows/go.yml 0000664 0000000 0000000 00000000661 14350614034 0022171 0 ustar 00root root 0000000 0000000 name: Go on: push: branches: - main - testing pull_request: branches: - main - testing jobs: build: name: Build runs-on: ubuntu-latest steps: - name: Set up Go 1.19 uses: actions/setup-go@v1 with: go-version: 1.19 id: go - name: Check out code into the Go module directory uses: actions/checkout@v2 - name: Build run: make build cs-firewall-bouncer-0.0.25/.github/workflows/release-drafter.yml 0000664 0000000 0000000 00000001130 14350614034 0024621 0 ustar 00root root 0000000 0000000 name: Release Drafter on: push: # branches to consider in the event; optional, defaults to all branches: - main jobs: update_release_draft: runs-on: ubuntu-latest steps: # Drafts your next Release notes as Pull Requests are merged into "main" - uses: release-drafter/release-drafter@v5 with: config-name: release-drafter.yml # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml # config-name: my-config.yml env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} cs-firewall-bouncer-0.0.25/.gitignore 0000664 0000000 0000000 00000000707 14350614034 0017435 0 ustar 00root root 0000000 0000000 # Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # Dependency directories (remove the comment below to include it) # vendor/ # built binaries /crowdsec-firewall-bouncer /crowdsec-firewall-bouncer.tgz /crowdsec-firewall-bouncer*/ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class venv/ cs-firewall-bouncer-0.0.25/.golangci.yml 0000664 0000000 0000000 00000025261 14350614034 0020033 0 ustar 00root root 0000000 0000000 # see https://github.com/golangci/golangci-lint/blob/master/.golangci.example.yml linters-settings: gocyclo: min-complexity: 30 funlen: # Checks the number of lines in a function. # If lower than 0, disable the check. # Default: 60 lines: -1 # Checks the number of statements in a function. # If lower than 0, disable the check. # Default: 40 statements: -1 govet: check-shadowing: true lll: line-length: 140 misspell: locale: US nolintlint: allow-leading-space: true # don't require machine-readable nolint directives (i.e. with no leading space) allow-unused: false # report any unused nolint directives require-explanation: false # don't require an explanation for nolint directives require-specific: false # don't require nolint directives to be specific about which linter is being skipped linters: enable-all: true disable: # # DEPRECATED by golangi-lint # - deadcode # The owner seems to have abandoned the linter. Replaced by unused. - exhaustivestruct # The owner seems to have abandoned the linter. Replaced by exhaustruct. - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes - ifshort # Checks that your code uses short syntax for if-statements whenever possible - interfacer # Linter that suggests narrower interface types - maligned # Tool to detect Go structs that would take less memory if their fields were sorted - nosnakecase # nosnakecase is a linter that detects snake case of variable naming and function name. - scopelint # Scopelint checks for unpinned variables in go programs - structcheck # The owner seems to have abandoned the linter. Replaced by unused. - varcheck # The owner seems to have abandoned the linter. Replaced by unused. # # Disabled # - gci # Gci control golang package import order and make it always deterministic. # # Enabled # # - asasalint # check for pass []any as any in variadic func(...any) # - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers # - bidichk # Checks for dangerous unicode character sequences # - bodyclose # checks whether HTTP response body is closed successfully # - containedctx # containedctx is a linter that detects struct contained context.Context field # - contextcheck # check the function whether use a non-inherited context # - decorder # check declaration order and count of types, constants, variables and functions # - depguard # Go linter that checks if package imports are in a list of acceptable packages # - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) # - durationcheck # check for two durations multiplied together # - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases # - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. # - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. # - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. # - execinquery # execinquery is a linter about query string checker in Query function which reads your Go src files and warning it finds # - exhaustive # check exhaustiveness of enum switch statements # - exportloopref # checks for pointers to enclosing loop variables # - forcetypeassert # finds forced type assertions # - funlen # Tool for detection of long functions # - gochecknoinits # Checks that no init functions are present in Go code # - godot # Check if comments end in a period # - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification # - goheader # Checks is file header matches to pattern # - goimports # In addition to fixing imports, goimports also formats your code in the same style as gofmt. # - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. # - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. # - goprintffuncname # Checks that printf-like functions are named with `f` at the end # - gosimple # (megacheck): Linter for Go source code that specializes in simplifying a code # - govet # (vet, vetshadow): Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string # - grouper # An analyzer to analyze expression groups. # - importas # Enforces consistent import aliases # - ineffassign # Detects when assignments to existing variables are not used # - interfacebloat # A linter that checks the number of methods inside an interface. # - lll # Reports long lines # - logrlint # Check logr arguments. # - makezero # Finds slice declarations with non-zero initial length # - misspell # Finds commonly misspelled English words in comments # - nakedret # Finds naked returns in functions greater than a specified function length # - nilerr # Finds the code that returns nil even if it checks that the error is not nil. # - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. # - noctx # noctx finds sending http request without context.Context # - nolintlint # Reports ill-formed or insufficient nolint directives # - nonamedreturns # Reports all named returns # - nosprintfhostport # Checks for misuse of Sprintf to construct a host with port in a URL. # - prealloc # Finds slice declarations that could potentially be preallocated # - predeclared # find code that shadows one of Go's predeclared identifiers # - promlinter # Check Prometheus metrics naming via promlint # - reassign # Checks that package variables are not reassigned # - rowserrcheck # checks whether Err of rows is checked successfully # - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. # - staticcheck # (megacheck): Staticcheck is a go vet on steroids, applying a ton of static analysis checks # - stylecheck # Stylecheck is a replacement for golint # - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 # - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers # - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes # - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code # - unconvert # Remove unnecessary type conversions # - unused # (megacheck): Checks Go code for unused constants, variables, functions and types # - usestdlibvars # A linter that detect the possibility to use variables/constants from the Go standard library. # - wastedassign # wastedassign finds wasted assignment statements. # # Recommended? (easy) # - gocritic # Provides diagnostics that check for bugs, performance and style issues. - gosec # (gas): Inspects source code for security problems - revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. - wrapcheck # Checks that errors returned from external packages are wrapped # # Recommended? (requires some work) # - gomnd # An analyzer to detect magic numbers. - ireturn # Accept Interfaces, Return Concrete Types - unparam # Reports unused function parameters # # Formatting only, useful in IDE but should not be forced on CI? # - gofumpt # Gofumpt checks whether code was gofumpt-ed. - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity - whitespace # Tool for detection of leading and trailing whitespace - wsl # Whitespace Linter - Forces you to use empty lines! # # Well intended, but not ready for this # - cyclop # checks function and package cyclomatic complexity - dupl # Tool for code clone detection - gocognit # Computes and checks the cognitive complexity of functions - gocyclo # Computes and checks the cyclomatic complexity of functions - godox # Tool for detection of FIXME, TODO and other comment keywords - goerr113 # Golang linter to check the errors handling expressions - maintidx # maintidx measures the maintainability index of each function. - nestif # Reports deeply nested if statements - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test - testpackage # linter that makes you use a separate _test package # # Too strict / too many false positives (for now?) # - exhaustruct # Checks if all structure fields are initialized - forbidigo # Forbids identifiers - gochecknoglobals # check that no global variables exist - goconst # Finds repeated strings that could be replaced by a constant - tagliatelle # Checks the struct tags. - varnamelen # checks that the length of a variable's name matches its scope issues: max-issues-per-linter: 0 max-same-issues: 10 exclude-rules: - path: go.mod text: "replacement are not allowed: github.com/koneu/natend" # `err` is often shadowed, we may continue to do it - linters: - govet text: "shadow: declaration of \"err\" shadows declaration" cs-firewall-bouncer-0.0.25/LICENSE 0000664 0000000 0000000 00000002062 14350614034 0016446 0 ustar 00root root 0000000 0000000 MIT License Copyright (c) 2020-2021 crowdsecurity 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. cs-firewall-bouncer-0.0.25/Makefile 0000664 0000000 0000000 00000005306 14350614034 0017105 0 ustar 00root root 0000000 0000000 # Go parameters GOCMD=go GOBUILD=$(GOCMD) build GOCLEAN=$(GOCMD) clean GOTEST=$(GOCMD) test GOGET=$(GOCMD) get # Current versioning information from env BUILD_VERSION?="$(shell git describe --tags)" BUILD_GOVERSION=$(shell go env GOVERSION | sed s/go//) BUILD_TIMESTAMP=$(shell date +%F"_"%T) BUILD_TAG?=$(shell git rev-parse HEAD) LD_OPTS_VARS=\ -X github.com/crowdsecurity/cs-firewall-bouncer/pkg/version.Version=$(BUILD_VERSION) \ -X github.com/crowdsecurity/cs-firewall-bouncer/pkg/version.BuildDate=$(BUILD_TIMESTAMP) \ -X github.com/crowdsecurity/cs-firewall-bouncer/pkg/version.Tag=$(BUILD_TAG) ifdef BUILD_STATIC export LD_OPTS=-ldflags "-a -s -w -extldflags '-static' $(LD_OPTS_VARS)" -tags netgo else export LD_OPTS=-ldflags "-a -s -w $(LD_OPTS_VARS)" endif PREFIX?="/" BINARY_NAME=crowdsec-firewall-bouncer MINIMUM_SUPPORTED_GO_MAJOR_VERSION = 1 MINIMUM_SUPPORTED_GO_MINOR_VERSION = 13 #Golang version info go_major_minor = $(subst ., ,$(BUILD_GOVERSION)) GO_MAJOR_VERSION = $(word 1, $(go_major_minor)) GO_MINOR_VERSION = $(word 2, $(go_major_minor)) 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) RELDIR = "crowdsec-firewall-bouncer-${BUILD_VERSION}" PYTHON=python3 PIP=pip all: clean build goversion: @if [ $(GO_MAJOR_VERSION) -gt $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION) ]; then \ exit 0 ;\ elif [ $(GO_MAJOR_VERSION) -lt $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION) ]; then \ echo '$(GO_VERSION_VALIDATION_ERR_MSG)';\ exit 1; \ elif [ $(GO_MINOR_VERSION) -lt $(MINIMUM_SUPPORTED_GO_MINOR_VERSION) ] ; then \ echo '$(GO_VERSION_VALIDATION_ERR_MSG)';\ exit 1; \ fi .PHONY: lint lint: golangci-lint run build: goversion clean $(GOBUILD) $(LD_OPTS) $(BUILD_VENDOR_FLAGS) -o $(BINARY_NAME) test: @$(GOTEST) $(LD_OPTS) ./... clean: @$(RM) $(BINARY_NAME) @$(RM) -r ${RELDIR} @$(RM) crowdsec-firewall-bouncer.tgz @$(RM) -r tests/venv .PHONY: func-tests func-tests: build ( \ $(PYTHON) -m venv tests/venv ; \ tests/venv/bin/$(PIP) install -r tests/requirements.txt ; \ sudo tests/venv/bin/$(PYTHON) -B -m unittest -v ; \ ) .PHONY: release release: build @if [ -z ${BUILD_VERSION} ] ; then BUILD_VERSION="local" ; fi @if [ -d $(RELDIR) ]; then echo "$(RELDIR) already exists, clean" ; exit 1 ; fi @echo Building Release to dir $(RELDIR) @mkdir $(RELDIR)/ @cp $(BINARY_NAME) $(RELDIR)/ @cp -R ./config $(RELDIR)/ @cp ./scripts/install.sh $(RELDIR)/ @cp ./scripts/uninstall.sh $(RELDIR)/ @cp ./scripts/upgrade.sh $(RELDIR)/ @chmod +x $(RELDIR)/install.sh @chmod +x $(RELDIR)/uninstall.sh @chmod +x $(RELDIR)/upgrade.sh @tar cvzf crowdsec-firewall-bouncer.tgz $(RELDIR) cs-firewall-bouncer-0.0.25/README.md 0000664 0000000 0000000 00000002154 14350614034 0016722 0 ustar 00root root 0000000 0000000
📚 Documentation 💠 Hub 💬 Discourse
# crowdsec-firewall-bouncer Crowdsec bouncer written in golang for firewalls. crowdsec-firewall-bouncer will fetch new and old decisions from a CrowdSec API to add them in a blocklist used by supported firewalls. Supported firewalls: - iptables (IPv4 :heavy_check_mark: / IPv6 :heavy_check_mark: ) - nftables (IPv4 :heavy_check_mark: / IPv6 :heavy_check_mark: ) - ipset only (IPv4 :heavy_check_mark: / IPv6 :heavy_check_mark: ) - pf (IPV4 :heavy_check_mark: / IPV6 :heavy_check_mark: ) # Installation Please follow the [official documentation](https://doc.crowdsec.net/docs/bouncers/firewall). cs-firewall-bouncer-0.0.25/backend.go 0000664 0000000 0000000 00000003664 14350614034 0017370 0 ustar 00root root 0000000 0000000 package main import ( "fmt" "runtime" log "github.com/sirupsen/logrus" "github.com/crowdsecurity/crowdsec/pkg/models" ) type backend interface { Init() error ShutDown() error Add(*models.Decision) error Delete(*models.Decision) error Commit() error CollectMetrics() } type backendCTX struct { firewall backend } func (b *backendCTX) Init() error { return b.firewall.Init() } func (b *backendCTX) Commit() error { return b.firewall.Commit() } func (b *backendCTX) ShutDown() error { return b.firewall.ShutDown() } func (b *backendCTX) Add(decision *models.Decision) error { return b.firewall.Add(decision) } func (b *backendCTX) Delete(decision *models.Decision) error { return b.firewall.Delete(decision) } func (b *backendCTX) CollectMetrics() { b.firewall.CollectMetrics() } func isPFSupported(runtimeOS string) bool { var supported bool switch runtimeOS { case "openbsd", "freebsd": supported = true default: supported = false } return supported } func newBackend(config *bouncerConfig) (*backendCTX, error) { var err error b := &backendCTX{} log.Printf("backend type : %s", config.Mode) if config.DisableIPV6 { log.Println("IPV6 is disabled") } switch config.Mode { case IptablesMode, IpsetMode: if runtime.GOOS != "linux" { return nil, fmt.Errorf("iptables and ipset is linux only") } b.firewall, err = newIPTables(config) if err != nil { return nil, err } case NftablesMode: if runtime.GOOS != "linux" { return nil, fmt.Errorf("nftables is linux only") } b.firewall, err = newNFTables(config) if err != nil { return nil, err } case "pf": if !isPFSupported(runtime.GOOS) { log.Warning("pf mode can only work with openbsd and freebsd. It is available on other platforms only for testing purposes") } b.firewall, err = newPF(config) if err != nil { return nil, err } default: return b, fmt.Errorf("firewall '%s' is not supported", config.Mode) } return b, nil } cs-firewall-bouncer-0.0.25/config.go 0000664 0000000 0000000 00000014625 14350614034 0017245 0 ustar 00root root 0000000 0000000 package main import ( "fmt" "io" log "github.com/sirupsen/logrus" "gopkg.in/natefinch/lumberjack.v2" "gopkg.in/yaml.v2" "github.com/crowdsecurity/crowdsec/pkg/types" "github.com/crowdsecurity/crowdsec/pkg/yamlpatch" ) type PrometheusConfig struct { Enabled bool `yaml:"enabled"` ListenAddress string `yaml:"listen_addr"` ListenPort string `yaml:"listen_port"` } type nftablesFamilyConfig struct { Enabled *bool `yaml:"enabled"` SetOnly bool `yaml:"set-only"` Table string `yaml:"table"` Chain string `yaml:"chain"` // Blacklist string `yaml:"blacklist"` } const ( IpsetMode = "ipset" IptablesMode = "iptables" NftablesMode = "nftables" PfMode = "pf" ) type bouncerConfig struct { Mode string `yaml:"mode"` // ipset,iptables,tc PidDir string `yaml:"pid_dir"` UpdateFrequency string `yaml:"update_frequency"` Daemon bool `yaml:"daemonize"` LogMode string `yaml:"log_mode"` LogDir string `yaml:"log_dir"` LogLevel log.Level `yaml:"log_level"` CompressLogs *bool `yaml:"compress_logs,omitempty"` LogMaxSize int `yaml:"log_max_size,omitempty"` LogMaxFiles int `yaml:"log_max_files,omitempty"` LogMaxAge int `yaml:"log_max_age,omitempty"` DisableIPV6 bool `yaml:"disable_ipv6"` DenyAction string `yaml:"deny_action"` DenyLog bool `yaml:"deny_log"` DenyLogPrefix string `yaml:"deny_log_prefix"` BlacklistsIpv4 string `yaml:"blacklists_ipv4"` BlacklistsIpv6 string `yaml:"blacklists_ipv6"` SetType string `yaml:"ipset_type"` SetSize int `yaml:"ipset_size"` // specific to iptables, following https://github.com/crowdsecurity/cs-firewall-bouncer/issues/19 IptablesChains []string `yaml:"iptables_chains"` SupportedDecisionsTypes []string `yaml:"supported_decisions_types"` // specific to nftables, following https://github.com/crowdsecurity/cs-firewall-bouncer/issues/74 Nftables struct { Ipv4 nftablesFamilyConfig `yaml:"ipv4"` Ipv6 nftablesFamilyConfig `yaml:"ipv6"` } `yaml:"nftables"` PF struct { AnchorName string `yaml:"anchor_name"` } `yaml:"pf"` PrometheusConfig PrometheusConfig `yaml:"prometheus"` } // mergedConfig() returns the byte content of the patched configuration file (with .yaml.local). func mergedConfig(configPath string) ([]byte, error) { patcher := yamlpatch.NewPatcher(configPath, ".local") data, err := patcher.MergedPatchContent() if err != nil { return nil, err } return data, nil } func newConfig(reader io.Reader) (*bouncerConfig, error) { config := &bouncerConfig{} fcontent, err := io.ReadAll(reader) if err != nil { return &bouncerConfig{}, err } err = yaml.Unmarshal(fcontent, &config) if err != nil { return &bouncerConfig{}, fmt.Errorf("failed to unmarshal: %w", err) } err = validateConfig(*config) if err != nil { return &bouncerConfig{}, err } if len(config.SupportedDecisionsTypes) == 0 { config.SupportedDecisionsTypes = []string{"ban"} } if config.PidDir == "" { log.Warningf("missing 'pid_dir' directive, using default: '/var/run/'") config.PidDir = "/var/run/" } if config.DenyLog && config.DenyLogPrefix == "" { config.DenyLogPrefix = "crowdsec drop: " } // for config file backward compatibility if config.BlacklistsIpv4 == "" { config.BlacklistsIpv4 = "crowdsec-blacklists" } if config.BlacklistsIpv6 == "" { config.BlacklistsIpv6 = "crowdsec6-blacklists" } if config.SetType == "" { config.SetType = "nethash" } if config.SetSize == 0 { config.SetSize = 65536 } switch config.Mode { case NftablesMode: err := nftablesConfig(config) if err != nil { return nil, err } case IpsetMode, IptablesMode: // nothing specific to do case PfMode: err := pfConfig(config) if err != nil { return nil, err } default: log.Warningf("unexpected %s mode", config.Mode) } return config, nil } func pfConfig(config *bouncerConfig) error { return nil } func nftablesConfig(config *bouncerConfig) error { // deal with defaults in a backward compatible way if config.Nftables.Ipv4.Enabled == nil { config.Nftables.Ipv4.Enabled = types.BoolPtr(true) } if config.Nftables.Ipv6.Enabled == nil { if config.DisableIPV6 { config.Nftables.Ipv4.Enabled = types.BoolPtr(false) } else { config.Nftables.Ipv6.Enabled = types.BoolPtr(true) } } if *config.Nftables.Ipv4.Enabled { if config.Nftables.Ipv4.Table == "" { config.Nftables.Ipv4.Table = "crowdsec" } if config.Nftables.Ipv4.Chain == "" { config.Nftables.Ipv4.Chain = "crowdsec-chain" } } if *config.Nftables.Ipv6.Enabled { if config.Nftables.Ipv6.Table == "" { config.Nftables.Ipv6.Table = "crowdsec6" } if config.Nftables.Ipv6.Chain == "" { config.Nftables.Ipv6.Chain = "crowdsec6-chain" } } if !*config.Nftables.Ipv4.Enabled && !*config.Nftables.Ipv6.Enabled { return fmt.Errorf("both IPv4 and IPv6 disabled, doing nothing") } return nil } func configureLogging(config *bouncerConfig) { var LogOutput *lumberjack.Logger // io.Writer /* Configure logging */ if err := types.SetDefaultLoggerConfig(config.LogMode, config.LogDir, config.LogLevel, config.LogMaxSize, config.LogMaxFiles, config.LogMaxAge, config.CompressLogs, false); err != nil { log.Fatal(err.Error()) } if config.LogMode == "file" { if config.LogDir == "" { config.LogDir = "/var/log/" } _maxsize := 500 if config.LogMaxSize != 0 { _maxsize = config.LogMaxSize } _maxfiles := 3 if config.LogMaxFiles != 0 { _maxfiles = config.LogMaxFiles } _maxage := 30 if config.LogMaxAge != 0 { _maxage = config.LogMaxAge } _compress := true if config.CompressLogs != nil { _compress = *config.CompressLogs } LogOutput = &lumberjack.Logger{ Filename: config.LogDir + "/crowdsec-firewall-bouncer.log", MaxSize: _maxsize, // megabytes MaxBackups: _maxfiles, MaxAge: _maxage, // days Compress: _compress, // disabled by default } log.SetOutput(LogOutput) log.SetFormatter(&log.TextFormatter{TimestampFormat: "02-01-2006 15:04:05", FullTimestamp: true}) } } func validateConfig(config bouncerConfig) error { if config.Mode == "" || config.LogMode == "" { return fmt.Errorf("config does not contain mode and log mode") } if config.LogMode != "stdout" && config.LogMode != "file" { return fmt.Errorf("log mode '%s' unknown, expecting 'file' or 'stdout'", config.LogMode) } return nil } cs-firewall-bouncer-0.0.25/config/ 0000775 0000000 0000000 00000000000 14350614034 0016706 5 ustar 00root root 0000000 0000000 cs-firewall-bouncer-0.0.25/config/crowdsec-firewall-bouncer.service 0000664 0000000 0000000 00000000604 14350614034 0025337 0 ustar 00root root 0000000 0000000 [Unit] Description=The firewall bouncer for CrowdSec After=syslog.target network.target remote-fs.target nss-lookup.target crowdsec.service Before=netfilter-persistent.service [Service] Type=notify ExecStart=${BIN} -c ${CFG}crowdsec-firewall-bouncer.yaml ExecStartPre=${BIN} -c ${CFG}crowdsec-firewall-bouncer.yaml -t ExecStartPost=/bin/sleep 0.1 [Install] WantedBy=multi-user.target cs-firewall-bouncer-0.0.25/config/crowdsec-firewall-bouncer.yaml 0000664 0000000 0000000 00000002004 14350614034 0024635 0 ustar 00root root 0000000 0000000 mode: ${BACKEND} pid_dir: /var/run/ update_frequency: 10s daemonize: true log_mode: file log_dir: /var/log/ log_level: info log_compression: true log_max_size: 100 log_max_backups: 3 log_max_age: 30 api_url: http://127.0.0.1:8080/ api_key: ${API_KEY} insecure_skip_verify: false disable_ipv6: false deny_action: DROP deny_log: false supported_decisions_types: - ban #to change log prefix #deny_log_prefix: "crowdsec: " #to change the blacklists name blacklists_ipv4: crowdsec-blacklists blacklists_ipv6: crowdsec6-blacklists #type of ipset to use ipset_type: nethash #if present, insert rule in those chains iptables_chains: - INPUT # - FORWARD # - DOCKER-USER ## nftables nftables: ipv4: enabled: true set-only: false table: crowdsec chain: crowdsec-chain ipv6: enabled: true set-only: false table: crowdsec6 chain: crowdsec6-chain # packet filter pf: # an empty string disables the anchor anchor_name: "" prometheus: enabled: true listen_addr: 127.0.0.1 listen_port: 60601 cs-firewall-bouncer-0.0.25/debian/ 0000775 0000000 0000000 00000000000 14350614034 0016663 5 ustar 00root root 0000000 0000000 cs-firewall-bouncer-0.0.25/debian/.gitignore 0000664 0000000 0000000 00000000217 14350614034 0020653 0 ustar 00root root 0000000 0000000 # Generated during the build /crowdsec-firewall-bouncer-iptables /crowdsec-firewall-bouncer-nftables /files /*.substvars /*.debhelper /*-stamp cs-firewall-bouncer-0.0.25/debian/changelog 0000664 0000000 0000000 00000000255 14350614034 0020537 0 ustar 00root root 0000000 0000000 crowdsec-firewall-bouncer (1.0.12) UNRELEASED; urgency=medium * debian package * pf support -- Manuel Sabban