2023-04-09 21:42:38 +02:00
# reaction
2023-10-22 12:00:00 +02:00
A daemon that scans program outputs for repeated patterns, and takes action.
2023-04-09 21:42:38 +02:00
2023-10-22 12:00:00 +02:00
A common usage is to scan ssh and webserver logs, and to ban hosts that cause multiple authentication errors.
2023-04-09 21:42:38 +02:00
2023-10-22 12:00:00 +02:00
🚧 This program hasn't received external audit. however, it already works well on my servers 🚧
2023-05-26 13:53:59 +02:00
2023-10-22 12:00:00 +02:00
## Rationale
2023-04-09 21:42:38 +02:00
2023-10-22 12:00:00 +02:00
I was using the honorable fail2ban since quite a long time, but i was a bit frustrated by its cpu consumption
2023-04-09 21:42:38 +02:00
and all its heavy default configuration.
2023-10-22 12:00:00 +02:00
In my view, a security-oriented program should be simple to configure
and an always-running daemon should be implemented in a fast*er* language.
2023-04-09 21:42:38 +02:00
2023-10-22 12:00:00 +02:00
reaction does not have all the features of the honorable fail2ban, but it's ~10x faster and has more manageable configuration.
2023-10-01 12:00:00 +02:00
2023-11-30 12:00:00 +01:00
[📽️ quick french name explanation 😉 ](https://u.ppom.me/reaction.webm )
2023-10-30 12:00:00 +01:00
2023-11-30 12:00:00 +01:00
[🇬🇧 in-depth blog article ](https://blog.ppom.me/en-reaction )
/ [🇫🇷 french version ](https://blog.ppom.me/fr-reaction )
2023-04-09 21:42:38 +02:00
2023-10-22 12:00:00 +02:00
## Configuration
2023-04-09 21:42:38 +02:00
2023-10-22 12:00:00 +02:00
YAML and [JSONnet ](https://jsonnet.org/ ) (more powerful) are supported.
both are extensions of JSON, so JSON is transitively supported.
2023-09-03 13:26:27 +02:00
2023-10-22 12:00:00 +02:00
- See [reaction.yml ](./app/example.yml ) or [reaction.jsonnet ](./config/example.jsonnet ) for a fully explained reference
- See [server.jsonnet ](./config/server.jsonnet ) for a real-world configuration
2023-12-20 12:00:00 +01:00
- See [reaction.example.service ](./config/reaction.example.service ) for a systemd service file
2023-10-22 12:00:00 +02:00
- This quick example shows what's needed to prevent brute force attacks on an ssh server:
< details open >
2023-04-09 21:42:38 +02:00
2023-10-22 12:00:00 +02:00
< summary > < code > /etc/reaction.yml< / code > < / summary >
```yaml
2023-04-09 21:42:38 +02:00
patterns:
2023-10-18 12:00:00 +02:00
ip: '(([ 0-9 ]{1,3}\.){3}[0-9]{1,3})|([0-9a-fA-F:]{2,90})'
start:
2023-10-22 12:00:00 +02:00
- [ 'ip46tables', '-w', '-N', 'reaction' ]
- [ 'ip46tables', '-w', '-A', 'reaction', '-j', 'ACCEPT' ]
- [ 'ip46tables', '-w', '-I', 'reaction', '1', '-s', '127.0.0.1', '-j', 'ACCEPT' ]
- [ 'ip46tables', '-w', '-I', 'INPUT', '-p', 'all', '-j', 'reaction' ]
2023-10-18 12:00:00 +02:00
stop:
2023-10-22 12:00:00 +02:00
- [ 'ip46tables', '-w', '-D', 'INPUT', '-p', 'all', '-j', 'reaction' ]
- [ 'ip46tables', '-w', '-F', 'reaction' ]
- [ 'ip46tables', '-w', '-X', 'reaction' ]
2023-04-09 21:42:38 +02:00
streams:
ssh:
2023-10-22 12:00:00 +02:00
cmd: [ 'journalctl', '-fu', 'sshd.service' ]
2023-04-09 21:42:38 +02:00
filters:
failedlogin:
regex:
2023-10-04 12:00:00 +02:00
- 'authentication failure;.*rhost=< ip > '
2023-04-09 21:42:38 +02:00
retry: 3
2023-10-04 12:00:00 +02:00
retryperiod: '6h'
2023-04-09 21:42:38 +02:00
actions:
ban:
2023-10-22 12:00:00 +02:00
cmd: [ 'ip46tables', '-w', '-I', 'reaction', '1', '-s', '< ip > ', '-j', 'block' ]
2023-04-09 21:42:38 +02:00
unban:
2023-10-22 12:00:00 +02:00
cmd: [ 'ip46tables', '-w', '-D', 'reaction', '1', '-s', '< ip > ', '-j', 'block' ]
2023-10-04 12:00:00 +02:00
after: '48h'
2023-04-09 21:42:38 +02:00
```
2023-10-22 12:00:00 +02:00
< / details >
< details >
< summary > < code > /etc/reaction.jsonnet< / code > < / summary >
2023-10-04 12:00:00 +02:00
```jsonnet
2023-10-18 12:00:00 +02:00
local iptables(args) = [ 'ip46tables', '-w' ] + args;
2023-10-22 12:00:00 +02:00
local banFor(time) = {
ban: {
cmd: iptables(['-A', 'reaction', '-s', '< ip > ', '-j', 'reaction-log-refuse']),
},
unban: {
after: time,
cmd: iptables(['-D', 'reaction', '-s', '< ip > ', '-j', 'reaction-log-refuse']),
},
};
2023-10-04 12:00:00 +02:00
{
patterns: {
ip: {
2023-10-18 12:00:00 +02:00
regex: @'(?:(?:[ 0-9 ]{1,3}\.){3}[0-9]{1,3})|(?:[0-9a-fA-F:]{2,90})',
2023-10-04 12:00:00 +02:00
},
},
2023-10-22 12:00:00 +02:00
start: [
iptables([ '-N', 'reaction' ]),
iptables([ '-A', 'reaction', '-j', 'ACCEPT' ]),
iptables([ '-I', 'reaction', '1', '-s', '127.0.0.1', '-j', 'ACCEPT' ]),
iptables([ '-I', 'INPUT', '-p', 'all', '-j', 'reaction' ]),
],
stop: [
2023-12-21 12:00:00 +01:00
iptables([ '-D', 'INPUT', '-p', 'all', '-j', 'reaction' ]),
iptables([ '-F', 'reaction' ]),
iptables([ '-X', 'reaction' ]),
2023-10-22 12:00:00 +02:00
],
2023-10-04 12:00:00 +02:00
streams: {
ssh: {
2023-10-18 12:00:00 +02:00
cmd: [ 'journalctl', '-fu', 'sshd.service' ],
2023-10-04 12:00:00 +02:00
filters: {
failedlogin: {
regex: [ @'authentication failure;.*rhost=< ip > ' ],
retry: 3,
retryperiod: '6h',
2023-10-22 12:00:00 +02:00
actions: banFor('48h'),
2023-10-04 12:00:00 +02:00
},
},
},
},
}
```
2023-10-22 12:00:00 +02:00
< / details >
2023-10-04 12:00:00 +02:00
2023-04-11 13:01:02 +02:00
2023-10-22 12:00:00 +02:00
### Database
2023-04-11 13:01:02 +02:00
2023-10-22 12:00:00 +02:00
The embedded database is stored in the working directory.
If you don't know where to start reaction, `/var/lib/reaction` should be a sane choice.
2023-04-11 13:01:02 +02:00
2023-10-22 12:00:00 +02:00
### CLI
2023-05-05 15:33:00 +02:00
2023-10-22 12:00:00 +02:00
- `reaction start` runs the server
- `reaction show` show pending actions (ie. bans)
- `reaction flush` permits to run pending actions (ie. clear bans)
- `reaction test-regex` permits to test regexes
- `reaction help` for full usage.
2023-05-05 15:33:00 +02:00
2023-10-05 12:00:00 +02:00
### `ip46tables`
`ip46tables` is a minimal c program present in its own subdirectory with only standard posix dependencies.
2023-10-22 12:00:00 +02:00
It permits to configure `iptables` and `ip6tables` at the same time.
It will execute `iptables` when detecting ipv4, `ip6tables` when detecting ipv6 and both if no ip address is present on the command line.
2023-10-05 12:00:00 +02:00
2023-11-30 12:00:00 +01:00
## Wiki
2024-01-17 12:00:00 +01:00
You'll find more ressources, service configurations, etc. on the [Wiki ](https://reaction.ppom.me )!
2023-11-30 12:00:00 +01:00
2023-10-28 12:00:00 +02:00
## Installation
2024-01-08 12:00:00 +01:00
[![Packaging status ](https://repology.org/badge/vertical-allrepos/reaction-fail2ban.svg )](https://repology.org/project/reaction-fail2ban/versions)
2023-10-28 12:00:00 +02:00
### Binaries
2023-12-05 12:00:00 +01:00
Executables are provided [here ](https://framagit.org/ppom/reaction/-/releases/ ), for a standard x86-64 linux machine.
2023-10-28 12:00:00 +02:00
2023-10-28 12:00:00 +02:00
A standard place to put such executables is `/usr/local/bin/` .
2024-01-10 12:00:00 +01:00
> Provided binaries in the previous section are compiled this way:
```shell
$ docker run -it --rm -e HOME=/tmp/ -v $(pwd):/tmp/code -w /tmp/code -u $(id -u) golang:1.20 make clean reaction.deb
$ make signaturese
```
2024-01-04 12:00:00 +01:00
#### Signature verification
Starting at v1.0.3, all binaries are signed with public key `RWSpLTPfbvllNqRrXUgZzM7mFjLUA7PQioAItz80ag8uU4A2wtoT2DzX` . You can check their authenticity with minisign:
```bash
minisign -VP RWSpLTPfbvllNqRrXUgZzM7mFjLUA7PQioAItz80ag8uU4A2wtoT2DzX -m ip46tables
minisign -VP RWSpLTPfbvllNqRrXUgZzM7mFjLUA7PQioAItz80ag8uU4A2wtoT2DzX -m reaction
# or
minisign -VP RWSpLTPfbvllNqRrXUgZzM7mFjLUA7PQioAItz80ag8uU4A2wtoT2DzX -m reaction.deb
```
2023-12-20 12:00:00 +01:00
#### Debian
The releases also contain a `reaction.deb` file, which packages reaction & ip46tables.
You can install it using `sudo apt install ./reaction.deb` .
You'll have to create a configuration at `/etc/reaction.jsonnet` .
If you want to use another configuration format (YAML or JSON), you can override systemd's `ExecStart` command in `/etc/systemd/system/reaction.service` like this:
```systemd
[Service]
# First an empty directive to reset the default one
ExecStart=
# Then put what you want
ExecStart=/usr/bin/reaction start -c /etc/reaction.yml
```
2024-01-10 12:00:00 +01:00
#### NixOS
- [ package ](https://framagit.org/ppom/nixos/-/blob/main/pkgs/reaction/default.nix )
- [ module ](https://framagit.org/ppom/nixos/-/blob/main/modules/common/reaction.nix )
2023-10-22 12:00:00 +02:00
### Compilation
2023-04-11 13:01:02 +02:00
2023-11-24 12:00:00 +01:00
You'll need the go (>= 1.20) toolchain for reaction and a c compiler for ip46tables.
2023-10-05 12:00:00 +02:00
```shell
$ make
```
2024-01-10 12:00:00 +01:00
Don't hesitate to take a look at the `Makefile` to understand what's happening!
2023-10-05 12:00:00 +02:00
2024-01-10 12:00:00 +01:00
### Installation
To install the binaries
2023-04-11 13:14:46 +02:00
```shell
2024-01-10 12:00:00 +01:00
make install
2023-04-09 21:42:38 +02:00
```
2023-05-26 13:53:59 +02:00
2024-01-10 12:00:00 +01:00
To install the systemd file as well
2023-10-28 12:00:00 +02:00
```shell
2024-01-10 12:00:00 +01:00
make install_systemd
2023-10-28 12:00:00 +02:00
```
2024-03-18 12:00:00 +01:00
## Development
Contributions are welcome. For any substantial feature, please file an issue first, to be assured that we agree on the feature, and to avoid unnecessary work.
This is a free time project, so I'm not working on schedule.
However, if you're willing to fund the project, I can priorise and plan paid work. This includes features, documentation and specific JSONnet configurations.