52 Commits
0.4.0 ... 0.7.1

Author SHA1 Message Date
08157eda73 docs: update changelog for release 2022-09-18 21:32:49 +01:00
8c38a10245 Merge branch '23-exporter-does-not-fail-when-listening-fails' into 'main'
Resolve "Exporter does not fail when listening fails"

Closes #23

See merge request hectorjsmith/fail2ban-prometheus-exporter!71
2022-09-18 20:29:33 +00:00
04b84cc840 fix: report failure when server fails to start (#23)
Exit with an error when the server startup reports an error. For example, when the server fails to bind to the provided port.
2022-09-18 21:26:22 +01:00
efa02e3c28 Merge branch 'release/0.7.0' into 'main'
Release/0.7.0

See merge request hectorjsmith/fail2ban-prometheus-exporter!70
2022-06-19 20:01:55 +00:00
6818f034c0 docs: update changelog for release 2022-06-19 17:18:13 +01:00
77b69da93c Merge branch '21-exit-exporter-when-fail2ban-is-restarted' into 'main'
Exit exporter when fail2ban is restarted

Closes #21

See merge request hectorjsmith/fail2ban-prometheus-exporter!69
2022-06-19 07:20:45 +00:00
fd58b20162 feat: option to exit on socket conn error (#21)
Add a new startup option to exit the exporter when an error occurs when connecting to the fail2ban socket file.
This option is set to "false" by default.
2022-06-19 07:20:45 +00:00
951ceccf67 Merge branch 'rename-project-module-to-include-url-path' into 'main'
Rename project module to include url path

See merge request hectorjsmith/fail2ban-prometheus-exporter!68
2022-02-25 22:01:55 +00:00
24ee5d96bd refactor: rename project module
Rename the project module to include the full project URL instead of just
the name. This better aligns with Go best-practices.
2022-02-25 21:53:38 +00:00
7c2bcecf7a Merge branch 'release/0.6.0' into 'main'
Release/0.6.0

See merge request hectorjsmith/fail2ban-prometheus-exporter!67
2022-02-20 17:13:25 +00:00
939fbf72f7 docs: update changelog for release 2022-02-20 16:59:16 +00:00
2759dcb159 Merge branch 'update-project-documentation' into 'main'
Update project documentation

See merge request hectorjsmith/fail2ban-prometheus-exporter!66
2022-02-20 16:44:27 +00:00
82b7e34866 docs: rewrite project readme
Rewrite the project README file to be more succinct and to the point. Add
new section on getting started quickly with either docker or the standalone
binary file.
Update CHANGELOG file with recent changes.
2022-02-20 16:44:27 +00:00
0eb4880286 Merge branch 'add-example-systemd-service-file' into 'main'
Add example systemd service file

See merge request hectorjsmith/fail2ban-prometheus-exporter!65
2022-02-20 09:05:38 +00:00
23e073ffde feat: add example systemd service file
Add a basic systemd service file to serve as an example on how to run the
exporter as a system service. Add short README with an overview of the
service file.
2022-02-20 09:05:38 +00:00
b12ab669b1 Merge branch 'update-goreleaser-config' into 'main'
Update goreleaser config

See merge request hectorjsmith/fail2ban-prometheus-exporter!64
2022-02-20 08:46:40 +00:00
3911eca07e feat: rename output binary and archives
Update the goreleaser config to rename the output binary and archive names
to `fail2ban_exporter` instead of defaulting to the project name. This
better aligns with conventions used by other exporters.
Update Dockerfile and Makefile to follow the new naming scheme.
Update the output archives to wrap the build files in a folder. This makes
extracting the archives a little easier.

BREAKING CHANGE: Release binary name has been changed to `fail2ban_exporter`.
2022-02-20 08:46:40 +00:00
cc68fe3f01 Merge branch 'add-mechanism-for-graceful-shutdown' into 'main'
Add mechanism for graceful shutdown

See merge request hectorjsmith/fail2ban-prometheus-exporter!63
2022-02-20 08:25:04 +00:00
f6e328a0aa feat: correctly handle shutdown signals
Add a new method to the application startup to listen for OS shutdown
signals and handle them appropriately. A shutdown signal will cause the
app to exit immediately.
Use correct syntax for the `ENTRYPOINT` field in the Dockerfile to ensure
that OS signals get passed down to the running application.
2022-02-20 08:17:06 +00:00
aedef536dd Merge branch 'update-changelog' into 'main'
Update changelog

See merge request hectorjsmith/fail2ban-prometheus-exporter!62
2022-02-19 17:10:25 +00:00
97ea58d86a docs: update changelog 2022-02-19 17:06:48 +00:00
ab80610a3a Merge branch 'rewrite-application-cli-parameters' into 'main'
Rewrite application cli parameters

See merge request hectorjsmith/fail2ban-prometheus-exporter!61
2022-02-19 17:01:50 +00:00
6e575aa0fd feat: rewrite cli flags and environment variables
Replace existing CLI flags to make them more consistent and follow a more
standard format.
Remove CLI flags and environment variables that are no longer relevant.
Add short `-v` option for version flag.
Update README with new documentation.

BREAKING CHANGE: Replace `--socket` flag with `--collector.f2b.socket`.
BREAKING CHANGE: Merge `--port` flag and `--web.listen-address` into a single flag.
BREAKING CHANGE: Remove `--collector.textfile` flag, its value is now derived from `--collector.textfile.directory`.
BREAKING CHANGE: Remove `F2B_COLLECTOR_TEXT` and `F2B_WEB_PORT` environment variables.
2022-02-19 17:01:49 +00:00
7515698ec8 Merge branch 'simplify-docker-image-for-exporter' into 'main'
Simplify docker image for exporter

See merge request hectorjsmith/fail2ban-prometheus-exporter!60
2022-02-19 14:58:51 +00:00
0f0efe58af feat: remove startup script from docker image
Update the docker image to remove the `run.sh` script and instead run the
exporter directly. This keeps the docker image as simple as possible.
Update README file with extra info on how to collect textfile metrics in
a docker container.

BREAKING CHANGE: Using the textfile collector in docker now requires setting environment variables.
2022-02-19 14:10:36 +00:00
e01d4cfe12 Merge branch 'update-exporter-logging-on-startup' into 'main'
Update exporter logging on startup

See merge request hectorjsmith/fail2ban-prometheus-exporter!59
2022-02-19 11:25:58 +00:00
e2902b8cc2 feat: improve logging on startup
Update the exporter logging on startup to include the exporter version,
the path to the fail2ban socket, and whether basic-auth is enabled or not.
Fix code printing error messages on invalid CLI parameters to correct line
breaks and correctly print the "usage" information.
2022-02-19 11:21:58 +00:00
ec10999814 Merge branch 'add-default-value-for-fail2ban-socket' into 'main'
Add default value for fail2ban socket

See merge request hectorjsmith/fail2ban-prometheus-exporter!58
2022-02-18 22:15:32 +00:00
1f27dace2d feat: add default value for fail2ban socket path
Update the CLI param parser to include a default value for the fail2ban
socket file path. This is the default location fail2ban creates the socket
file on an ubuntu-based system.
2022-02-18 22:15:32 +00:00
8452caf4c5 Merge branch 'update-changelog' into 'main'
Update changelog

See merge request hectorjsmith/fail2ban-prometheus-exporter!57
2022-02-18 21:52:01 +00:00
4bf63a6ed6 docs: update changelog
Update the CHANGELOG file with recent unreleased changes.
2022-02-18 21:49:20 +00:00
9f0327b028 Merge branch '19-update-creation-of-docker-image-labels' into 'main'
Resolve "Update creation of docker image labels"

Closes #19

See merge request hectorjsmith/fail2ban-prometheus-exporter!56
2022-02-12 17:31:01 +00:00
05f236902a ci: update creation of tags (#19)
Update creation of docker tags to only tag actual releases with the
`:latest` tag. Builds on the main branch are now tagged with a `:nightly`
tag.
2022-02-12 17:31:01 +00:00
191dda3b9f Merge branch '18-dash-missing-in-docker-run-sh-line-4-version' into 'main'
Resolve "Dash missing in /docker/run.sh line 4 "-version""

Closes #18

See merge request hectorjsmith/fail2ban-prometheus-exporter!55
2022-01-31 22:51:13 +00:00
93da909b0a fix: use correct flag in dockerfile (#18)
Update the Dockerfile entrypoint script to use the correct version flag
syntax. The flag now requires two dashes instead of one. The syntax for
the texfile collector has also change, and the script was updated
accordingly.
Fix incorrect order of variable assignment when parsing CLI flags.
2022-01-31 22:51:13 +00:00
98d376ac60 Merge branch '17-configure-exporter-using-environment-variables' into 'main'
Resolve "Configure exporter using environment variables"

Closes #17

See merge request hectorjsmith/fail2ban-prometheus-exporter!54
2022-01-30 21:32:48 +00:00
b7e317edbc feat: configure tool using environment variables (#17)
Replace CLI parsing functionality with the `kingpin` library to better
support configuring the tool with environment variables.
Add new environment variables to configure the tool.

BREAKING CHANGE: CLI params now require two dashes instead of one (e.g. `--socket`)
2022-01-30 21:32:48 +00:00
d92f7f79b6 Merge branch '16-add-basic-auth' into 'main'
Resolve "Add basic auth"

Closes #16

See merge request hectorjsmith/fail2ban-prometheus-exporter!53
2022-01-14 21:36:49 +00:00
6f76a03118 feat: add support for basic auth (#16)
Add new CLI parameters to enable protecting the API endpoints with basic
auth authentication.
Wrap the server endpoints in a new auth middleware that protects it using
the provided basic auth credentials (if set).
Store the provided basic auth credentials as hashed values to prevent them
from being accidentally leaked.
Add unit tests to ensure the new functionality works as expected.
2022-01-14 21:36:49 +00:00
013e8f30c9 Merge branch '15-update-sample-grafana-dashboard' into 'main'
Update sample grafana dashboard

Closes #15

See merge request hectorjsmith/fail2ban-prometheus-exporter!52
2021-12-23 11:42:10 +00:00
ae08a798b6 refactor: update grafana dashboard (#15)
Update the sample Grafana dashboard to use a variable for the data
source. All panels have been updated to use the new variable. This makes
it easier to import the dashboard and have it work with different data
sources.
2021-12-23 11:39:15 +00:00
cf71dc7449 Merge branch 'release/0.5.0' into 'main'
Release/0.5.0

See merge request hectorjsmith/fail2ban-prometheus-exporter!51
2021-12-21 21:25:00 +00:00
e0531694dd docs: update changelog for release 2021-12-21 21:16:38 +00:00
c2bc99afc2 Merge branch 'remove-deprecated-database-metric-collector' into 'main'
Remove deprecated database metric collector

See merge request hectorjsmith/fail2ban-prometheus-exporter!50
2021-12-21 17:46:46 +00:00
497e2ff692 remove: references to db collector
Remove final references to the deprecated database metric collector.
Remove counter for db connection errors.

BREAKING CHANGE: Remove `-db` CLI flag.
2021-12-21 17:42:42 +00:00
157e065369 Merge branch '15-add-sample-grafana-dashboard' into 'main'
Resolve "Add sample grafana dashboard"

Closes #15

See merge request hectorjsmith/fail2ban-prometheus-exporter!49
2021-12-21 17:29:00 +00:00
b397a51cf8 feat: sample grafana dashboard (#15)
Add a sample Grafana dashboard to display all the metrics collected by
this tool.
Update the README file to mention Grafana dashboard.
2021-12-21 17:29:00 +00:00
4be463a7c8 Merge branch 'python2' into 'main'
fix: support python2 fail2ban

Closes #14

See merge request hectorjsmith/fail2ban-prometheus-exporter!48
2021-12-18 06:58:40 +00:00
7932ccbe23 fix: support python2 fail2ban
Python2 pickles use different class names for some types. Specifically,
builtins.str is __builtin__.str.
2021-12-18 06:58:40 +00:00
6bbdd7a0a6 Merge branch 'main' into 'main'
feat: Add listen address parameter

See merge request hectorjsmith/fail2ban-prometheus-exporter!47
2021-12-18 06:45:37 +00:00
c208c8e97d feat: add listen address parameter
Add new -web.listen-address command line parameter, so that the
listening interface can be limited. This follows a similar style as
the official prometheus-node-exporter project.
Update project README with the new parameter.
2021-12-18 06:45:37 +00:00
f18bd78d4e Merge branch 'release/0.4.0' into 'main'
Release/0.4.0

See merge request hectorjsmith/fail2ban-prometheus-exporter!46
2021-10-18 18:44:46 +00:00
24 changed files with 1396 additions and 254 deletions

View File

@ -1,28 +1,41 @@
image: golang:latest
before_script:
- make go/dependencies
stages:
- test
- build
.go_template:
image: golang:latest
before_script:
- make go/dependencies
.docker_template:
image: docker:stable
services:
- docker:dind
before_script:
- apk add git
- apk add make
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
dependencies:
extends: .go_template
stage: test
script:
- make go/checkDependencies
format:
extends: .go_template
stage: test
script:
- make go/checkFmt
test:
extends: .go_template
stage: test
script:
- make go/test
build:
extends: .go_template
stage: build
only:
- main
@ -37,18 +50,20 @@ build:
expire_in: 1 day
docker/gitlab:
extends: .docker_template
stage: build
only:
- main
- tags
image: docker:stable
services:
- docker:dind
before_script:
- apk add git
- apk add make
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- make docker/build/latest
- make docker/build/tag
- docker push registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter
docker/gitlab/nightly:
extends: .docker_template
stage: build
only:
- main
script:
- make docker/build/nightly
- docker push registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter

View File

@ -1,10 +1,12 @@
# This is an example goreleaser.yaml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
project_name: fail2ban_exporter
before:
hooks:
- make go/dependencies
builds:
-
binary: fail2ban_exporter
dir: src
goos:
- linux
@ -20,6 +22,7 @@ builds:
archives:
-
wrap_in_directory: true
files:
- LICENSE
- README.md

View File

@ -7,6 +7,56 @@ The format is based on [Keep a Changelog], and this project adheres to [Semantic
## [Unreleased]
*Nothing yet*
## [0.7.1] - 2022-09-18
### Fixed
- (04b84cc) fix: report failure when server fails to start ([#23](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/issues/23))
## [0.7.0] - 2022-06-19
### Added
- (fd58b20) feat: option to exit on socket conn error ([#21](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/issues/21))
## [0.6.0] - 2022-02-20
*Rewrite CLI flags and environment variables*
### Added
- (23e073f) feat: add example systemd service file
- (3911eca) feat: rename output binary and archives
- (f6e328a) feat: correctly handle shutdown signals
- (6e575aa) feat: rewrite cli flags and environment variables
- (0f0efe5) feat: remove startup script from docker image
- (e2902b8) feat: improve logging on startup
- (1f27dac) feat: add default value for fail2ban socket path
- (b7e317e) feat: configure tool using environment variables ([#17](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/issues/17))
- (6f76a03) feat: add support for basic auth ([#16](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/issues/16))
### Fixed
- (93da909) fix: use correct flag in dockerfile ([#18](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/issues/18))
### BREAKING CHANGE
- Release binary name has been changed to `fail2ban_exporter`.
- Replace `--socket` flag with `--collector.f2b.socket`.
- Merge `--port` flag and `--web.listen-address` into a single flag.
- Remove `--collector.textfile` flag, its value is now derived from `--collector.textfile.directory`.
- Remove `F2B_COLLECTOR_TEXT` and `F2B_WEB_PORT` environment variables.
- Using the textfile collector in docker now requires setting environment variables.
- CLI params now require two dashes instead of one (e.g. `--socket`).
## [0.5.0] - 2021-12-21
*Remove deprecated code & support python2*
### Added
- (b397a51) feat: sample grafana dashboard ([#15](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/issues/15))
- (c208c8e) feat: add listen address parameter - thanks [@private-creator](https://gitlab.com/private-creator)!
### Fixed
- (7932ccb) fix: support python2 fail2ban ([#14](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/issues/14)) - thanks [@private-creator](https://gitlab.com/private-creator)!
### BREAKING CHANGE
- Remove `-db` CLI flag
- Remove `f2b_errors{type="db"}` metric
## [0.4.0] - 2021-10-18
*Add new fail2ban config metrics*
@ -65,8 +115,12 @@ The format is based on [Keep a Changelog], and this project adheres to [Semantic
[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
[Semantic Versioning]: https://semver.org/spec/v2.0.0.html
[Unreleased]: https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/compare/0.1.0...main
[Unreleased]: https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/compare/0.7.1...main
[0.1.0]: https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/compare/0.0.0...0.1.0
[0.2.0]: https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/compare/0.1.0...0.2.0
[0.3.0]: https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/compare/0.2.0...0.3.0
[0.4.0]: https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/compare/0.3.0...0.4.0
[0.5.0]: https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/compare/0.4.0...0.5.0
[0.6.0]: https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/compare/0.5.0...0.6.0
[0.7.0]: https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/compare/0.6.0...0.7.0
[0.7.1]: https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/compare/0.7.0...0.7.1

View File

@ -18,9 +18,6 @@ FROM debian:buster-slim
WORKDIR /app
# Copy compiled binary to release image
COPY --from=build /build/src/exporter /app/fail2ban-prometheus-exporter
COPY --from=build /build/src/fail2ban_exporter /app/fail2ban_exporter
# Copy init script into main app folder and set as entry point
COPY docker/run.sh /app/
RUN chmod +x /app/*
ENTRYPOINT /app/run.sh
ENTRYPOINT ["/app/fail2ban_exporter"]

View File

@ -26,11 +26,14 @@ build/release:
./tools/goreleaser_linux_amd64 --rm-dist --skip-publish
build/docker:
cd src/ && go build -o exporter \
cd src/ && go build -o fail2ban_exporter \
-ldflags '-X main.version=$(shell git describe --tags) -X main.commit=${shell git rev-parse HEAD} -X "main.date=${shell date --rfc-3339=seconds}" -X main.builtBy=docker' exporter.go
docker/build/latest:
docker build -t registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:latest .
docker/build/nightly:
docker build -t registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:nightly .
docker/build/tag:
docker build -t registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:$(shell git describe --tags) .

284
README.md
View File

@ -1,92 +1,39 @@
# Fail2Ban Prometheus Exporter
Go tool to collect and export metrics on Fail2Ban
[![Pipeline](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/badges/main/pipeline.svg)](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter)
Collect metrics from a running fail2ban instance.
## Table of Contents
1. Introduction
2. Running the Exporter
3. Running in Docker
4. Metrics
1. Quick Start
2. Metrics
3. Configuration
4. Building from source
5. Textfile metrics
## 1. Introduction
The exporter can collect metrics from 2 locations: the fail2ban server socket and the fail2ban server database.
## 1. Quick Start
Once the exporter is running, metrics are available at `localhost:9191/metrics`.
The exporter can be run as a standalone binary or a docker container.
(The default port is `9191` but can be modified with the `-port` flag)
### 1.1. Standalone
The exporter communicates with the fail2ban server over its socket.
This allows the data collected by the exporter to always align with the output of the `fail2ban-client`.
The default location of the socket is: `/var/run/fail2ban/fail2ban.sock`
## 2. Running the Exporter
The exporter is compiled and released as a single binary.
This makes it very easy to run in any environment.
No additional runtime dependencies are required.
Compiled binaries for various platforms are provided in each release.
See the [releases page](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/releases) for more information.
**Usage**
```
$ fail2ban-prometheus-exporter -h
-port int
port to use for the metrics server (default 9191)
-socket string
path to the fail2ban server socket
-version
show version info and exit
-collector.textfile
enable the textfile collector
-collector.textfile.directory string
directory to read text files with metrics from
```
**Example**
The following command will start collecting metrics from the `/var/run/fail2ban/fail2ban.sock` file and expose them on port `9191`.
```
fail2ban-prometheus-exporter -socket /var/run/fail2ban/fail2ban.sock -port 9191
$ fail2ban_exporter --collector.f2b.socket=/var/run/fail2ban/fail2ban.sock --web.listen-address=":9191"
2022/02/20 09:54:06 fail2ban exporter version 0.5.0
2022/02/20 09:54:06 starting server at :9191
2022/02/20 09:54:06 reading metrics from fail2ban socket: /var/run/fail2ban/fail2ban.sock
2022/02/20 09:54:06 metrics available at '/metrics'
2022/02/20 09:54:06 ready
```
Note that the exporter will need read access to the fail2ban socket.
Binary files for each release can be found on the [releases](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/releases) page.
### 2.1. Compile from Source
The code can be compiled from source by running `go build` inside the `src/` folder.
Go version `1.15` or greater is required.
Run `go mod download` to download all necessary dependencies before running the build.
## 3. Running in Docker
An official docker image is available on the Gitlab container registry.
Use it by pulling the following image:
```
registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:latest
```
Use the `:latest` tag to get the most up to date code (less stable) or use one of the version tagged images to use a specific release.
See the [registry page](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/container_registry) for all available tags.
### 3.1. Volumes
The docker image is designed to run by mounting the fail2ban run folder.
The run folder should be mounted in the container at: `/var/run/fail2ban`.
The folder can be mounted with read-only (`ro`) permissions.
**NOTE:** While it is possible to mount the `fail2ban.sock` file directly, it is recommended to mount the parent folder instead.
The `.sock` file is deleted by fail2ban on shutdown and re-created on startup and this causes problems for the docker mount.
See [this reply](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/issues/11#note_665003499) for more details.
### 3.2. Docker run
Use the following command to run the exporter as a docker container.
### 1.2. Docker
**Docker run**
```
docker run -d \
--name "fail2ban-exporter" \
@ -95,9 +42,7 @@ docker run -d \
registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:latest
```
### 3.3. Docker compose
The following is a simple docker-compose file to run the exporter.
**Docker compose**
```
version: "2"
@ -110,92 +55,39 @@ services:
- "9191:9191"
```
## 4. Metrics
Use the `:latest` tag to get the latest stable release. Or use the `:nightly` tag for the latest (unstable) version.
See the [registry page](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/container_registry) for all available tags.
Access exported metrics at the `/metrics` path on the configured port.
**NOTE:** While it is possible to mount the `fail2ban.sock` file directly, it is recommended to mount the parent folder instead.
The `.sock` file is deleted by fail2ban on shutdown and re-created on startup and this causes problems for the docker mount.
See [this reply](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/issues/11#note_665003499) for more details.
**Note on Fail2Ban Jails**
## 2. Metrics
fail2ban can be configured to process different log files and use different rules for each one.
These separate configurations are referred to as *jails*.
The exporter exposes the following metrics:
For example, fail2ban can be configured to watch the system logs for failed SSH connections and Nextcloud logs for failed logins.
In this configuration, there will be two jails - one for IPs banned from the SSH logs, and one for IPs banned from the Nextcloud logs.
*All metric names are prefixed with `f2b_`*
This tool exports several metrics *per jail*, meaning that it is possible to track how many IPs are being banned in each jail as well as the overall total.
This can be useful to track what services are seeing more failed logins.
### 4.1. Socket-based Metrics
These are the metrics exported by reading data from the fail2ban server socket.
All metrics are prefixed with `f2b_`.
Exposed metrics:
* `up` - Returns 1 if the fail2ban server is up and connection succeeds
* `errors` - Number of errors since startup
* `db` - Errors connecting to the database
* `socket_conn` - Errors connecting to the fail2ban socket (e.g. connection refused)
* `socket_req` - Errors sending requests to the fail2ban server (e.g. invalid responses)
* `jail_count` - Number of jails configured in fail2ban
* `jail_banned_current` (per jail) - Number of IPs currently banned
* `jail_banned_total` (per jail) - Total number of banned IPs since fail2ban startup (includes expired bans)
* `jail_failed_current` (per jail) - Number of current failures
* `jail_failed_total` (per jail) - Total number of failures since fail2ban startup
* `jail_config_ban_time` (per jail) - How long an IP is banned for in this jail (in seconds)
* `jail_config_find_time` (per jail) - How far back the filter will look for failures in this jail (in seconds)
* `jail_config_max_retry` (per jail) - The max number of failures allowed before banning an IP in this jail
* `version` - Version string of the exporter and fail2ban
**Sample**
```
# HELP f2b_errors Number of errors found since startup
# TYPE f2b_errors counter
f2b_errors{type="db"} 0
f2b_errors{type="socket_conn"} 0
f2b_errors{type="socket_req"} 0
# HELP f2b_jail_banned_current Number of IPs currently banned in this jail
# TYPE f2b_jail_banned_current gauge
f2b_jail_banned_current{jail="recidive"} 5
f2b_jail_banned_current{jail="sshd"} 15
# HELP f2b_jail_banned_total Total number of IPs banned by this jail (includes expired bans)
# TYPE f2b_jail_banned_total gauge
f2b_jail_banned_total{jail="recidive"} 6
f2b_jail_banned_total{jail="sshd"} 31
# HELP f2b_jail_count Number of defined jails
# TYPE f2b_jail_count gauge
f2b_jail_count 2
# HELP f2b_jail_failed_current Number of current failures on this jail's filter
# TYPE f2b_jail_failed_current gauge
f2b_jail_failed_current{jail="recidive"} 5
f2b_jail_failed_current{jail="sshd"} 6
# HELP f2b_jail_failed_total Number of total failures on this jail's filter
# TYPE f2b_jail_failed_total gauge
f2b_jail_failed_total{jail="recidive"} 7
f2b_jail_failed_total{jail="sshd"} 125
# HELP f2b_config_jail_ban_time How long an IP is banned for in this jail (in seconds)
# TYPE f2b_config_jail_ban_time gauge
f2b_config_jail_ban_time{jail="recidive"} 604800
f2b_config_jail_ban_time{jail="sshd"} 600
# HELP f2b_config_jail_find_time How far back will the filter look for failures in this jail (in seconds)
# TYPE f2b_config_jail_find_time gauge
f2b_config_jail_find_time{jail="recidive"} 86400
f2b_config_jail_find_time{jail="sshd"} 600
# HELP f2b_config_jail_max_retries The number of failures allowed until the IP is banned by this jail
# TYPE f2b_config_jail_max_retries gauge
f2b_config_jail_max_retries{jail="recidive"} 5
f2b_config_jail_max_retries{jail="sshd"} 5
# HELP f2b_up Check if the fail2ban server is up
# TYPE f2b_up gauge
f2b_up 1
# HELP f2b_version Version of the exporter and fail2ban server
# TYPE f2b_version gauge
f2b_version{exporter="0.3.0",fail2ban="0.11.1"} 1
```
| Metric | Description | Example |
|------------------------------|------------------------------------------------------------------------------------|-----------------------------------------------------|
| `up` | Returns 1 if the exporter is up and running | `f2b_up 1` |
| `errors` | Count the number of errors since startup by type | |
| `errors{type="socket_conn"}` | Errors connecting to the fail2ban socket (e.g. connection refused) | `f2b_errors{type="socket_conn"} 0` |
| `errors{type="socket_req"}` | Errors sending requests to the fail2ban server (e.g. invalid responses) | `f2b_errors{type="socket_req"} 0` |
| `jail_count` | Number of jails configured in fail2ban | `f2b_jail_count 2` |
| `jail_banned_current` | Number of IPs currently banned per jail | `f2b_jail_banned_current{jail="sshd"} 15` |
| `jail_banned_total` | Total number of banned IPs since fail2ban startup per jail (includes expired bans) | `f2b_jail_banned_total{jail="sshd"} 31` |
| `jail_failed_current` | Number of current failures per jail | `f2b_jail_failed_current{jail="sshd"} 6` |
| `jail_failed_total` | Total number of failures since fail2ban startup per jail | `f2b_jail_failed_total{jail="sshd"} 125` |
| `jail_config_ban_time` | How long an IP is banned for in this jail (in seconds) | `f2b_config_jail_ban_time{jail="sshd"} 600` |
| `jail_config_find_time` | How far back the filter will look for failures in this jail (in seconds) | `f2b_config_jail_find_time{jail="sshd"} 600` |
| `jail_config_max_retry` | The max number of failures allowed before banning an IP in this jail | `f2b_config_jail_max_retries{jail="sshd"} 5` |
| `version` | Version string of the exporter and fail2ban | `f2b_version{exporter="0.5.0",fail2ban="0.11.1"} 1` |
The metrics above correspond to the matching fields in the `fail2ban-client status <jail>` command:
```
Status for the jail: sshd|- Filter
Status for the jail: sshd
|- Filter
| |- Currently failed: 6
| |- Total failed: 125
| `- File list: /var/log/auth.log
@ -205,13 +97,68 @@ Status for the jail: sshd|- Filter
`- Banned IP list: ...
```
### 4.2. Textfile Metrics
### 2.1. Grafana
The metrics exported by this tool are compatible with Prometheus and Grafana.
A sample grafana dashboard can be found in the [grafana.json](/examples/grafana/dashboard.json) file.
Just import the contents of this file into a new Grafana dashboard to get started.
*(Sample dashboard is compatible with Grafana `8.3.3` and above)*
## 3. Configuration
The exporter is configured with CLI flags and environment variables.
There are no configuration files.
**CLI flags**
```
usage: exporter [<flags>]
Flags:
-h, --help Show context-sensitive help (also try --help-long and --help-man).
-v, --version show version info and exit
--collector.f2b.socket="/var/run/fail2ban/fail2ban.sock"
path to the fail2ban server socket
--collector.textfile.directory=""
directory to read text files with metrics from
--web.listen-address=":9191"
address to use for the metrics server
--web.basic-auth.username=""
username to use to protect endpoints with basic auth
--web.basic-auth.password=""
password to use to protect endpoints with basic auth
--collector.f2b.exit-on-socket-connection-error
when set to true the exporter will immediately exit on a fail2ban socket connection error
```
**Environment variables**
Each environment variable corresponds to a CLI flag.
If both are specified, the CLI flag takes precedence.
| Environment variable | Corresponding CLI flag |
|---------------------------------|---------------------------------------------------|
| `F2B_COLLECTOR_SOCKET` | `--collector.f2b.socket` |
| `F2B_COLLECTOR_TEXT_PATH` | `--collector.textfile.directory` |
| `F2B_WEB_LISTEN_ADDRESS` | `--web.listen-address` |
| `F2B_WEB_BASICAUTH_USER` | `--web.basic-auth.username` |
| `F2B_WEB_BASICAUTH_PASS` | `--web.basic-auth.password` |
| `F2B_EXIT_ON_SOCKET_CONN_ERROR` | `--collector.f2b.exit-on-socket-connection-error` |
## 4. Building from source
The simplest way to build the project is to run the `build/snapshot` make command.
This will use `goreleaser` to build out binaries and archives for the project.
Binaries are stored in the `dist/` folder.
Alternatively, `go mod download` and `go build` can be used from the `src/` folder to build out the project.
This will download dependencies and build the project.
## 5. Textfile metrics
For more flexibility the exporter also allows exporting metrics collected from a text file.
To enable textfile metrics:
1. Enable the collector with `-collector.textfile=true`
2. Provide the directory to read files from with the `-collector.textfile.directory` flag
To enable textfile metrics provide the directory to read files from with the `--collector.textfile.directory` flag.
Metrics collected from these files will be exposed directly alongside the other metrics without any additional processing.
This means that it is the responsibility of the file creator to ensure the format is correct.
@ -225,3 +172,20 @@ textfile_error{path="file.prom"} 0
```
**NOTE:** Any file not ending with `.prom` will be ignored.
**Running in Docker**
To collect textfile metrics inside a docker container, a couple of things need to be done:
1. Mount the folder with the metrics files
2. Set the `F2B_COLLECTOR_TEXT_PATH` environment variable
*For example:*
```
docker run -d \
--name "fail2ban-exporter" \
-v /var/run/fail2ban:/var/run/fail2ban:ro \
-v /path/to/metrics:/app/metrics/:ro \
-e F2B_COLLECTOR_TEXT_PATH=/app/metrics \
-p "9191:9191" \
registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:latest
```

View File

@ -1,20 +0,0 @@
#/bin/sh
# Print version to logs for debugging purposes
/app/fail2ban-prometheus-exporter -version
socket_path=/var/run/fail2ban/fail2ban.sock
textfile_dir=/app/textfile/
textfile_enabled=false
# Enable textfile metrics if the folder exists (i.e. was mounted by docker)
if [ -d $textfile_dir ]; then
textfile_enabled=true
fi
# Start the exporter (use exec to support graceful shutdown)
# Inspired by: https://akomljen.com/stopping-docker-containers-gracefully/
exec /app/fail2ban-prometheus-exporter \
-socket "$socket_path" \
-collector.textfile=$textfile_enabled \
-collector.textfile.directory="$textfile_dir"

View File

@ -0,0 +1,807 @@
{
"__inputs": [
{
"name": "DS_PROMETHEUS",
"label": "Prometheus",
"description": "",
"type": "datasource",
"pluginId": "prometheus",
"pluginName": "Prometheus"
}
],
"__elements": [],
"__requires": [
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "8.3.3"
},
{
"type": "datasource",
"id": "prometheus",
"name": "Prometheus",
"version": "1.0.0"
},
{
"type": "panel",
"id": "table",
"name": "Table",
"version": ""
},
{
"type": "panel",
"id": "timeseries",
"name": "Time series",
"version": ""
}
],
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 2,
"id": null,
"iteration": 1640159475017,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byRegexp",
"options": ".*Time"
},
"properties": [
{
"id": "unit",
"value": "s"
}
]
}
]
},
"gridPos": {
"h": 6,
"w": 24,
"x": 0,
"y": 0
},
"id": 206,
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "8.3.3",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"exemplar": false,
"expr": "f2b_config_jail_max_retries",
"format": "table",
"instant": true,
"interval": "",
"legendFormat": "{{jail}}",
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"exemplar": false,
"expr": "f2b_config_jail_ban_time",
"format": "table",
"hide": false,
"instant": true,
"interval": "",
"legendFormat": "{{jail}}",
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"exemplar": false,
"expr": "f2b_config_jail_find_time",
"format": "table",
"hide": false,
"instant": true,
"interval": "",
"legendFormat": "{{jail}}",
"refId": "C"
}
],
"title": "F2B Config",
"transformations": [
{
"id": "merge",
"options": {}
},
{
"id": "groupBy",
"options": {
"fields": {
"Value #A": {
"aggregations": [
"lastNotNull"
],
"operation": "aggregate"
},
"Value #B": {
"aggregations": [
"lastNotNull"
],
"operation": "aggregate"
},
"Value #C": {
"aggregations": [
"lastNotNull"
],
"operation": "aggregate"
},
"jail": {
"aggregations": [],
"operation": "groupby"
}
}
}
},
{
"id": "organize",
"options": {
"excludeByName": {},
"indexByName": {},
"renameByName": {
"Value #A (lastNotNull)": "Max Retries",
"Value #B (lastNotNull)": "Ban Time",
"Value #C (lastNotNull)": "Find Time",
"jail": "Jail"
}
}
}
],
"transparent": true,
"type": "table"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 6
},
"id": 190,
"options": {
"legend": {
"calcs": [
"lastNotNull"
],
"displayMode": "table",
"placement": "right"
},
"tooltip": {
"mode": "single"
}
},
"pluginVersion": "8.2.1",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"exemplar": true,
"expr": "f2b_jail_failed_total",
"hide": false,
"interval": "",
"legendFormat": "{{jail}}",
"refId": "A"
}
],
"title": "Fail2Ban Failures (Total)",
"transparent": true,
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 6
},
"id": 191,
"options": {
"legend": {
"calcs": [
"lastNotNull"
],
"displayMode": "table",
"placement": "right"
},
"tooltip": {
"mode": "single"
}
},
"pluginVersion": "8.2.1",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"exemplar": true,
"expr": "f2b_jail_banned_total",
"interval": "",
"legendFormat": "{{jail}}",
"refId": "A"
}
],
"title": "Fail2Ban Bans (Total)",
"transparent": true,
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 14
},
"id": 208,
"options": {
"legend": {
"calcs": [
"lastNotNull"
],
"displayMode": "table",
"placement": "right"
},
"tooltip": {
"mode": "single"
}
},
"pluginVersion": "8.2.1",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"exemplar": true,
"expr": "f2b_jail_failed_current",
"interval": "",
"legendFormat": "{{jail}}",
"refId": "A"
}
],
"title": "Fail2Ban Failures (Current)",
"transparent": true,
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 14
},
"id": 209,
"options": {
"legend": {
"calcs": [
"lastNotNull"
],
"displayMode": "table",
"placement": "right"
},
"tooltip": {
"mode": "single"
}
},
"pluginVersion": "8.2.1",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"exemplar": true,
"expr": "f2b_jail_banned_current",
"interval": "",
"legendFormat": "{{jail}}",
"refId": "A"
}
],
"title": "Fail2Ban Bans (Current)",
"transparent": true,
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"max": 1,
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 12,
"x": 0,
"y": 22
},
"id": 203,
"options": {
"legend": {
"calcs": [
"lastNotNull"
],
"displayMode": "table",
"placement": "right"
},
"tooltip": {
"mode": "single"
}
},
"pluginVersion": "8.2.1",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"exemplar": true,
"expr": "f2b_up",
"interval": "",
"legendFormat": "Up",
"refId": "A"
}
],
"title": "Fail2Ban Up",
"transparent": true,
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"max": 1,
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 12,
"x": 12,
"y": 22
},
"id": 204,
"options": {
"legend": {
"calcs": [
"lastNotNull"
],
"displayMode": "table",
"placement": "right"
},
"tooltip": {
"mode": "single"
}
},
"pluginVersion": "8.2.1",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"exemplar": true,
"expr": "f2b_errors",
"interval": "",
"legendFormat": "{{type}}",
"refId": "A"
}
],
"title": "Fail2Ban Exporter Errors",
"transparent": true,
"type": "timeseries"
}
],
"refresh": "30s",
"schemaVersion": 34,
"style": "dark",
"tags": [],
"templating": {
"list": [
{
"current": {
"selected": true,
"text": "Prometheus",
"value": "Prometheus"
},
"hide": 0,
"includeAll": false,
"label": "Data Source",
"multi": false,
"name": "DataSource",
"options": [],
"query": "prometheus",
"queryValue": "",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"type": "datasource"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "F2B",
"uid": "cTkH9AT7z",
"version": 17,
"weekStart": ""
}

View File

@ -0,0 +1,7 @@
# Systemd
The `.service` file in this directory should be copied to the `/etc/systemd/system/` folder.
- It expects the binary file to be installed at `/usr/sbin/fail2ban_exporter`.
- It expects a user named `fail2ban_exporter` to exist. This user should not have a shell or any special privileges aside from read-access to the fail2ban socket file.
The `ExecStart` line can be modified to add any custom CLI flags.

View File

@ -0,0 +1,9 @@
[Unit]
Description=Fail2Ban Exporter
[Service]
User=fail2ban_exporter
ExecStart=/usr/sbin/fail2ban_exporter
[Install]
WantedBy=multi-user.target

18
src/auth/hash.go Normal file
View File

@ -0,0 +1,18 @@
package auth
import (
"crypto/sha256"
"encoding/hex"
)
func Hash(data []byte) []byte {
if len(data) == 0 {
return []byte{}
}
b := sha256.Sum256(data)
return b[:]
}
func HashString(data string) string {
return hex.EncodeToString(Hash([]byte(data)))
}

26
src/auth/hash_test.go Normal file
View File

@ -0,0 +1,26 @@
package auth
import (
"reflect"
"testing"
)
func TestHashString(t *testing.T) {
tests := []struct {
name string
args string
want string
}{
{"Happy path #1", "123", "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3"},
{"Happy path #2", "hello world", "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"},
{"Happy path #3", "H3Ll0_W0RLD", "d58a27fe9a6e73a1d8a67189fb8acace047e7a1a795276a0056d3717ad61bd0e"},
{"Blank string", "", ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := HashString(tt.args); !reflect.DeepEqual(got, tt.want) {
t.Errorf("HashString() = %v, want %v", got, tt.want)
}
})
}
}

31
src/auth/middleware.go Normal file
View File

@ -0,0 +1,31 @@
package auth
import (
"net/http"
)
type BasicAuthProvider interface {
Enabled() bool
DoesBasicAuthMatch(username, password string) bool
}
func BasicAuthMiddleware(handlerFunc http.HandlerFunc, basicAuthProvider BasicAuthProvider) http.HandlerFunc {
if basicAuthProvider.Enabled() {
return func(w http.ResponseWriter, r *http.Request) {
if doesBasicAuthMatch(r, basicAuthProvider) {
handlerFunc.ServeHTTP(w, r)
} else {
w.WriteHeader(http.StatusUnauthorized)
}
}
}
return handlerFunc
}
func doesBasicAuthMatch(r *http.Request, basicAuthProvider BasicAuthProvider) bool {
rawUsername, rawPassword, ok := r.BasicAuth()
if ok {
return basicAuthProvider.DoesBasicAuthMatch(rawUsername, rawPassword)
}
return false
}

View File

@ -0,0 +1,58 @@
package auth
import (
"net/http"
"net/http/httptest"
"testing"
)
type testAuthProvider struct {
enabled bool
match bool
}
func (p testAuthProvider) Enabled() bool {
return p.enabled
}
func (p testAuthProvider) DoesBasicAuthMatch(username, password string) bool {
return p.match
}
func newTestRequest() *http.Request {
return httptest.NewRequest(http.MethodGet, "http://example.com", nil)
}
func executeBasicAuthMiddlewareTest(t *testing.T, authEnabled bool, authMatches bool, expectedCode int, expectedCallCount int) {
callCount := 0
testHandler := func(w http.ResponseWriter, r *http.Request) {
callCount++
}
handler := BasicAuthMiddleware(testHandler, testAuthProvider{enabled: authEnabled, match: authMatches})
recorder := httptest.NewRecorder()
request := newTestRequest()
if authEnabled {
request.SetBasicAuth("test", "test")
}
handler.ServeHTTP(recorder, request)
if recorder.Code != expectedCode {
t.Errorf("statusCode = %v, want %v", recorder.Code, expectedCode)
}
if callCount != expectedCallCount {
t.Errorf("callCount = %v, want %v", callCount, expectedCallCount)
}
}
func Test_GIVEN_DisabledBasicAuth_WHEN_MethodCalled_THEN_RequestProcessed(t *testing.T) {
executeBasicAuthMiddlewareTest(t, false, false, http.StatusOK, 1)
}
func Test_GIVEN_EnabledBasicAuth_WHEN_MethodCalledWithCorrectCredentials_THEN_RequestProcessed(t *testing.T) {
executeBasicAuthMiddlewareTest(t, true, true, http.StatusOK, 1)
}
func Test_GIVEN_EnabledBasicAuth_WHEN_MethodCalledWithIncorrectCredentials_THEN_RequestRejected(t *testing.T) {
executeBasicAuthMiddlewareTest(t, true, false, http.StatusUnauthorized, 0)
}

25
src/cfg/basicAuth.go Normal file
View File

@ -0,0 +1,25 @@
package cfg
import "gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/auth"
type hashedBasicAuth struct {
username string
password string
}
func newHashedBasicAuth(rawUsername, rawPassword string) *hashedBasicAuth {
return &hashedBasicAuth{
username: auth.HashString(rawUsername),
password: auth.HashString(rawPassword),
}
}
func (p *hashedBasicAuth) Enabled() bool {
return len(p.username) > 0 && len(p.password) > 0
}
func (p *hashedBasicAuth) DoesBasicAuthMatch(rawUsername, rawPassword string) bool {
username := auth.HashString(rawUsername)
password := auth.HashString(rawPassword)
return username == p.username && password == p.password
}

60
src/cfg/basicAuth_test.go Normal file
View File

@ -0,0 +1,60 @@
package cfg
import "testing"
func Test_hashedBasicAuth_DoesBasicAuthMatch(t *testing.T) {
type args struct {
username string
password string
}
type fields struct {
username string
password string
}
tests := []struct {
name string
fields fields
args args
want bool
}{
{"Happy test #1", fields{username: "1234", password: "1234"}, args{username: "1234", password: "1234"}, true},
{"Happy test #2", fields{username: "test", password: "1234"}, args{username: "test", password: "1234"}, true},
{"Happy test #3", fields{username: "TEST", password: "1234"}, args{username: "TEST", password: "1234"}, true},
{"Non match #1", fields{username: "test", password: "1234"}, args{username: "1234", password: "1234"}, false},
{"Non match #2", fields{username: "1234", password: "test"}, args{username: "1234", password: "1234"}, false},
{"Non match #3", fields{username: "1234", password: "test"}, args{username: "1234", password: "TEST"}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
basicAuth := newHashedBasicAuth(tt.fields.username, tt.fields.password)
if got := basicAuth.DoesBasicAuthMatch(tt.args.username, tt.args.password); got != tt.want {
t.Errorf("DoesBasicAuthMatch() = %v, want %v", got, tt.want)
}
})
}
}
func Test_hashedBasicAuth_Enabled(t *testing.T) {
type fields struct {
username string
password string
}
tests := []struct {
name string
fields fields
want bool
}{
{"Both blank", fields{username: "", password: ""}, false},
{"Single blank #1", fields{username: "test", password: ""}, false},
{"Single blank #1", fields{username: "", password: "test"}, false},
{"Both populated", fields{username: "test", password: "test"}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
basicAuth := newHashedBasicAuth(tt.fields.username, tt.fields.password)
if got := basicAuth.Enabled(); got != tt.want {
t.Errorf("Enabled() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -1,59 +1,109 @@
package cfg
import (
"flag"
"fmt"
"gopkg.in/alecthomas/kingpin.v2"
"os"
)
const (
minServerPort = 1000
maxServerPort = 65535
socketEnvName = "F2B_COLLECTOR_SOCKET"
fileCollectorPathEnvName = "F2B_COLLECTOR_TEXT_PATH"
addressEnvName = "F2B_WEB_LISTEN_ADDRESS"
basicAuthUserEnvName = "F2B_WEB_BASICAUTH_USER"
basicAuthPassEnvName = "F2B_WEB_BASICAUTH_PASS"
exitOnSocketConnErrorEnvName = "F2B_EXIT_ON_SOCKET_CONN_ERROR"
)
type AppSettings struct {
VersionMode bool
MetricsPort int
MetricsAddress string
Fail2BanSocketPath string
FileCollectorPath string
FileCollectorEnabled bool
BasicAuthProvider *hashedBasicAuth
ExitOnSocketConnError bool
}
func init() {
kingpin.HelpFlag.Short('h')
}
func Parse() *AppSettings {
appSettings := &AppSettings{}
flag.BoolVar(&appSettings.VersionMode, "version", false, "show version info and exit")
flag.IntVar(&appSettings.MetricsPort, "port", 9191, "port to use for the metrics server")
flag.StringVar(&appSettings.Fail2BanSocketPath, "socket", "", "path to the fail2ban server socket")
flag.BoolVar(&appSettings.FileCollectorEnabled, "collector.textfile", false, "enable the textfile collector")
flag.StringVar(&appSettings.FileCollectorPath, "collector.textfile.directory", "", "directory to read text files with metrics from")
settings := &AppSettings{}
readParamsFromCli(settings)
settings.validateFlags()
return settings
}
// deprecated: to be removed in next version
_ = flag.String("db", "", "path to the fail2ban sqlite database (removed)")
func readParamsFromCli(settings *AppSettings) {
versionMode := kingpin.
Flag("version", "show version info and exit").
Short('v').
Default("false").
Bool()
socketPath := kingpin.
Flag("collector.f2b.socket", "path to the fail2ban server socket").
Default("/var/run/fail2ban/fail2ban.sock").
Envar(socketEnvName).
String()
fileCollectorPath := kingpin.
Flag("collector.textfile.directory", "directory to read text files with metrics from").
Default("").
Envar(fileCollectorPathEnvName).
String()
address := kingpin.
Flag("web.listen-address", "address to use for the metrics server").
Default(":9191").
Envar(addressEnvName).
String()
rawBasicAuthUsername := kingpin.
Flag("web.basic-auth.username", "username to use to protect endpoints with basic auth").
Default("").
Envar(basicAuthUserEnvName).
String()
rawBasicAuthPassword := kingpin.
Flag("web.basic-auth.password", "password to use to protect endpoints with basic auth").
Default("").
Envar(basicAuthPassEnvName).
String()
rawExitOnSocketConnError := kingpin.
Flag("collector.f2b.exit-on-socket-connection-error", "when set to true the exporter will immediately exit on a fail2ban socket connection error").
Default("false").
Envar(exitOnSocketConnErrorEnvName).
Bool()
flag.Parse()
appSettings.validateFlags()
return appSettings
kingpin.Parse()
settings.VersionMode = *versionMode
settings.MetricsAddress = *address
settings.Fail2BanSocketPath = *socketPath
settings.FileCollectorPath = *fileCollectorPath
settings.setBasicAuthValues(*rawBasicAuthUsername, *rawBasicAuthPassword)
settings.ExitOnSocketConnError = *rawExitOnSocketConnError
}
func (settings *AppSettings) setBasicAuthValues(rawUsername, rawPassword string) {
settings.BasicAuthProvider = newHashedBasicAuth(rawUsername, rawPassword)
}
func (settings *AppSettings) validateFlags() {
var flagsValid = true
if !settings.VersionMode {
if settings.Fail2BanSocketPath == "" {
fmt.Println("fail2ban socket path must not be blank")
fmt.Println("error: fail2ban socket path must not be blank")
flagsValid = false
}
if settings.MetricsPort < minServerPort || settings.MetricsPort > maxServerPort {
fmt.Printf("invalid server port, must be within %d and %d (found %d)\n",
minServerPort, maxServerPort, settings.MetricsPort)
if settings.MetricsAddress == "" {
fmt.Println("error: invalid server address, must not be blank")
flagsValid = false
}
if settings.FileCollectorEnabled && settings.FileCollectorPath == "" {
fmt.Printf("file collector directory path must not be empty if collector enabled\n")
if (len(settings.BasicAuthProvider.username) > 0) != (len(settings.BasicAuthProvider.password) > 0) {
fmt.Println("error: to enable basic auth both the username and the password must be provided")
flagsValid = false
}
}
if !flagsValid {
flag.Usage()
kingpin.Usage()
os.Exit(1)
}
}

View File

@ -1,29 +1,31 @@
package f2b
import (
"fail2ban-prometheus-exporter/cfg"
"fail2ban-prometheus-exporter/socket"
"github.com/prometheus/client_golang/prometheus"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/cfg"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/socket"
"log"
"os"
)
type Collector struct {
socketPath string
exporterVersion string
lastError error
dbErrorCount int
socketConnectionErrorCount int
socketRequestErrorCount int
exitOnSocketConnError bool
}
func NewExporter(appSettings *cfg.AppSettings, exporterVersion string) *Collector {
log.Printf("reading metrics from fail2ban socket: %s", appSettings.Fail2BanSocketPath)
return &Collector{
socketPath: appSettings.Fail2BanSocketPath,
exporterVersion: exporterVersion,
lastError: nil,
dbErrorCount: 0,
socketConnectionErrorCount: 0,
socketRequestErrorCount: 0,
exitOnSocketConnError: appSettings.ExitOnSocketConnError,
}
}
@ -42,6 +44,9 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) {
if err != nil {
log.Printf("error opening socket: %v", err)
c.socketConnectionErrorCount++
if c.exitOnSocketConnError {
os.Exit(1)
}
} else {
defer s.Close()
}

View File

@ -1,8 +1,8 @@
package f2b
import (
"fail2ban-prometheus-exporter/socket"
"github.com/prometheus/client_golang/prometheus"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/socket"
"log"
)
@ -69,9 +69,6 @@ var (
)
func (c *Collector) collectErrorCountMetric(ch chan<- prometheus.Metric) {
ch <- prometheus.MustNewConstMetric(
metricErrorCount, prometheus.CounterValue, float64(c.dbErrorCount), "db",
)
ch <- prometheus.MustNewConstMetric(
metricErrorCount, prometheus.CounterValue, float64(c.socketConnectionErrorCount), "socket_conn",
)

View File

@ -1,8 +1,8 @@
package textfile
import (
"fail2ban-prometheus-exporter/cfg"
"github.com/prometheus/client_golang/prometheus"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/cfg"
"log"
)
@ -20,12 +20,12 @@ type fileData struct {
func NewCollector(appSettings *cfg.AppSettings) *Collector {
collector := &Collector{
enabled: appSettings.FileCollectorEnabled,
enabled: appSettings.FileCollectorPath != "",
folderPath: appSettings.FileCollectorPath,
fileMap: make(map[string]*fileData),
}
if collector.enabled {
log.Printf("collector.textfile directory: %s", collector.folderPath)
log.Printf("reading textfile metrics from: %s", collector.folderPath)
}
return collector
}

View File

@ -1,15 +1,18 @@
package main
import (
"fail2ban-prometheus-exporter/cfg"
"fail2ban-prometheus-exporter/collector/f2b"
"fail2ban-prometheus-exporter/collector/textfile"
"fmt"
"log"
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/auth"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/cfg"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/collector/f2b"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/collector/textfile"
"log"
"net/http"
"os"
"os/signal"
"syscall"
)
const (
@ -53,9 +56,9 @@ func main() {
if appSettings.VersionMode {
printAppVersion()
} else {
addr := fmt.Sprintf("0.0.0.0:%d", appSettings.MetricsPort)
log.Printf("starting fail2ban exporter at %s", addr)
handleGracefulShutdown()
log.Printf("fail2ban exporter version %s", version)
log.Printf("starting server at %s", appSettings.MetricsAddress)
f2bCollector := f2b.NewExporter(appSettings, version)
prometheus.MustRegister(f2bCollector)
@ -63,19 +66,38 @@ func main() {
textFileCollector := textfile.NewCollector(appSettings)
prometheus.MustRegister(textFileCollector)
http.HandleFunc("/", rootHtmlHandler)
http.HandleFunc(metricsPath, func(w http.ResponseWriter, r *http.Request) {
http.HandleFunc("/", auth.BasicAuthMiddleware(rootHtmlHandler, appSettings.BasicAuthProvider))
http.HandleFunc(metricsPath, auth.BasicAuthMiddleware(
func(w http.ResponseWriter, r *http.Request) {
metricHandler(w, r, textFileCollector)
})
},
appSettings.BasicAuthProvider,
))
log.Printf("metrics available at '%s'", metricsPath)
if appSettings.BasicAuthProvider.Enabled() {
log.Printf("basic auth enabled")
}
svrErr := make(chan error)
go func() {
svrErr <- http.ListenAndServe(addr, nil)
svrErr <- http.ListenAndServe(appSettings.MetricsAddress, nil)
}()
log.Print("ready")
err := <-svrErr
log.Print(err)
log.Fatal(err)
}
}
func handleGracefulShutdown() {
var signals = make(chan os.Signal)
signal.Notify(signals, syscall.SIGTERM)
signal.Notify(signals, syscall.SIGINT)
go func() {
sig := <-signals
log.Printf("caught signal: %+v", sig)
os.Exit(0)
}()
}

View File

@ -1,4 +1,4 @@
module fail2ban-prometheus-exporter
module gitlab.com/hectorjsmith/fail2ban-prometheus-exporter
go 1.15
@ -6,4 +6,5 @@ require (
github.com/kisielk/og-rek v1.1.0
github.com/nlpodyssey/gopickle v0.1.0
github.com/prometheus/client_golang v1.9.0
gopkg.in/alecthomas/kingpin.v2 v2.2.6
)

View File

@ -7,9 +7,11 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
@ -40,6 +42,7 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@ -142,8 +145,10 @@ github.com/kisielk/og-rek v1.1.0/go.mod h1:6ihsOSzSAxR/65S3Bn9zNihoEqRquhDQZ2c6I
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
@ -202,6 +207,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@ -255,6 +261,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
@ -379,9 +386,11 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
@ -395,6 +404,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -59,7 +59,7 @@ func (s *Fail2BanSocket) read() (interface{}, error) {
unpickler := pickle.NewUnpickler(bufReader)
unpickler.FindClass = func(module, name string) (interface{}, error) {
if module == "builtins" && name == "str" {
if (module == "builtins" || module == "__builtin__") && name == "str" {
return &Py_builtins_str{}, nil
}
return nil, fmt.Errorf("class not found: " + module + " : " + name)