# reaction
A daemon that scans program outputs for repeated patterns, and takes action.
A common usage is to scan ssh and webserver logs, and to ban hosts that cause multiple authentication errors.
π§ This program hasn't received external security audit. However, it already works well on my servers π§
## Rationale
I was using the honorable fail2ban since quite a long time, but i was a bit frustrated by its CPU consumption
and all its heavy default configuration.
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.
reaction does not have all the features of the honorable fail2ban, but it's more than 10x faster and has more manageable configuration.
[π½οΈ quick french name explanation π](https://u.ppom.me/reaction.webm)
[π¬π§ in-depth blog article](https://blog.ppom.me/en-reaction)
/ [π«π· french version](https://blog.ppom.me/fr-reaction)
## Rust rewrite
reaction v2.x is a complete Rust rewrite of reaction.
It's in feature parity with the Go version, v1.x, which is now deprecated.
See https://blog.ppom.me/en-reaction-v2.
## Configuration
YAML and [JSONnet](https://jsonnet.org/) (more powerful) are supported.
both are extensions of JSON, so JSON is transitively supported.
- See [reaction.yml](./config/example.yml) or [reaction.jsonnet](./config/example.jsonnet) for a fully explained reference (ipv4 + ipv6)
- See the [wiki](https://reaction.ppom.me) for multiple examples, security recommendations and FAQ.
- See [server.jsonnet](https://reaction.ppom.me/configurations/ppom/server.jsonnet.html) for a real-world configuration
- See [reaction.service](./config/reaction.service) for a systemd service file
- This minimal example (ipv4 only) shows what's needed to prevent brute force attacks on an ssh server (please read at least the [Security](https://reaction.ppom.me/security.html) part of the wiki before starting π):
/etc/reaction.yml
```yaml
patterns:
ip:
type: ipv4
start:
- [ 'iptables', '-w', '-N', 'reaction' ]
- [ 'iptables', '-w', '-I', 'INPUT', '-p', 'all', '-j', 'reaction' ]
- [ 'iptables', '-w', '-I', 'FORWARD', '-p', 'all', '-j', 'reaction' ]
stop:
- [ 'iptables', '-w', '-D', 'INPUT', '-p', 'all', '-j', 'reaction' ]
- [ 'iptables', '-w', '-D', 'FORWARD', '-p', 'all', '-j', 'reaction' ]
- [ 'iptables', '-w', '-F', 'reaction' ]
- [ 'iptables', '-w', '-X', 'reaction' ]
streams:
ssh:
cmd: [ 'journalctl', '-fu', 'sshd.service' ]
filters:
failedlogin:
regex:
- 'authentication failure;.*rhost='
- 'Failed password for .* from '
- 'Invalid user .* from ',
- 'banner exchange: Connection from port [0-9]*: invalid format',
retry: 3
retryperiod: '6h'
actions:
ban:
cmd: [ 'iptables', '-w', '-I', 'reaction', '1', '-s', '', '-j', 'DROP' ]
unban:
cmd: [ 'iptables', '-w', '-D', 'reaction', '1', '-s', '', '-j', 'DROP' ]
after: '48h'
```
/etc/reaction.jsonnet
```jsonnet
local banFor(time) = {
ban: {
cmd: ['iptables', '-w', '-A', 'reaction', '-s', '', '-j', 'DROP'],
},
unban: {
cmd: ['iptables', '-w', '-D', 'reaction', '-s', '', '-j', 'DROP'],
after: time,
},
};
{
patterns: {
ip: {
type: 'ipv4',
},
},
start: [
['iptables', '-N', 'reaction'],
['iptables', '-I', 'INPUT', '-p', 'all', '-j', 'reaction'],
['iptables', '-I', 'FORWARD', '-p', 'all', '-j', 'reaction'],
],
stop: [
['iptables', '-D', 'INPUT', '-p', 'all', '-j', 'reaction'],
['iptables', '-D', 'FORWARD', '-p', 'all', '-j', 'reaction'],
['iptables', '-F', 'reaction'],
['iptables', '-X', 'reaction'],
],
streams: {
ssh: {
cmd: ['journalctl', '-fu', 'sshd.service'],
filters: {
failedlogin: {
regex: [
@'authentication failure;.*rhost=',
@'Failed password for .* from ',
@'banner exchange: Connection from port [0-9]*: invalid format',
@'Invalid user .* from ',
],
retry: 3,
retryperiod: '6h',
actions: banFor('48h'),
},
},
},
},
}
```
### Database
The embedded database is stored in the working directory (but can be overriden by the `state_directory` config option).
If you don't know where to start reaction, `/var/lib/reaction` should be a sane choice.
### CLI
- `reaction start` runs the server
- `reaction show` show pending actions (ie. show current bans)
- `reaction flush` permits to run pending actions (ie. clear bans)
- `reaction trigger` permits to manually trigger a filter (ie. run custom ban)
- `reaction test-regex` permits to test regexes
- `reaction test-config` shows loaded configuration
- `reaction help` for full usage.
### `ip46tables` and `nft46`
> β οΈDeprecated since v2.2.0:
> reaction now provides builtin support for executing different actions on ipv4 and ipv6.
> They will be removed in a future version.
`ip46tables` and `nft46` are two minimal c programs present in the `helpers_c` directory with only standard posix dependencies.
`ip46tables` 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.
`nft46` works slightly differently: it will replace the `X` in its argument by 4 or 6 depending on the ip address on the command line.
This permits to have 2 IP sets, one of type `ipv4_addr` and one of type `ipv6_addr`.
## Wiki
You'll find more ressources, service configurations, etc. on [the wiki](https://reaction.ppom.me)!
We recommend that you read the ***Good Practices*** chapters before starting.
## Installation
[](https://repology.org/project/reaction-fail2ban/versions)
### Binaries
Executables and .deb packages are provided [in the releases page](https://framagit.org/ppom/reaction/-/releases/), for x86-64/amd64 linux and aarch64/arm64 linux.
Signature verification and installation instructions are provided in the releases page.
> Provided binaries are compiled by running `nix-shell release.py` on a NixOS machine with docker installed.
#### NixOS
reaction is packaged, but the [**module**](https://framagit.org/ppom/nixos/-/blob/main/modules/common/reaction.nix) has not yet been upstreamed.
#### OpenBSD
See the [wiki](https://reaction.ppom.me/configurations/OpenBSD.html).
### Compilation
You'll need a recent rust toolchain for reaction and a c compiler for ip46tables.
```shell
$ make
```
Don't hesitate to take a look at the `Makefile` to understand what's happening!
### Installation
To install the binaries
```shell
make install
```
To install the systemd file as well
```shell
make install_systemd
```
## Contributing
> We, as participants in the open source ecosystem, are ethically responsible for the software
> and hardware we help create - as it can be used to perpetuate inequalities or help empower
> marginalized communities, and fight against patriarchy, capitalism, sexism, gender violence,
> racism, ableism, homophobia, colonialism, fascism, surveillance, and oppressive control.
- [NGI's Diversity and Inclusion Guide](https://nlnet.nl/NGI0/bestpractices/DiversityAndInclusionGuide-v4.pdf)
I'll do my best to maintain a safe contribution place, as free as possible from discrimination and elitism.
### Ideas
Please take a look at issues which have the "Opinion Welcome π" label!
*Your opinion is welcome.*
Your ideas are welcome in the issues.
### Code
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.
I recommend reading [`ARCHITECTURE.md`](ARCHITECTURE.md) first. This is a quick tour of the codebase, which should save time to new contributors.
You can also join this Matrix development room: [#reaction-dev-en:club1.fr](https://matrix.to/#/#reaction-dev-en:club1.fr).
French version: [#reaction-dev-fr:club1.fr](https://matrix.to/#/#reaction-dev-fr:club1.fr).
## Help
You can ask for help in the issues or in this Matrix room: [#reaction-users-en:club1.fr](https://matrix.to/#/#reaction-users-en:club1.fr).
French version: [#reaction-users-fr:club1.fr](https://matrix.to/#/#reaction-users-fr:club1.fr).
## Funding
This project is currenlty funded through the [NGI0 Core](https://nlnet.nl/core) Fund, a fund established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) programme.
