Scan logs and take action
  • Rust 93.8%
  • Jsonnet 3.7%
  • Python 2%
  • Makefile 0.3%
  • Shell 0.1%
Find a file
2026-02-12 12:00:00 +01:00
bench cargo build builds plugin 2025-12-07 12:00:00 +01:00
config systemd: Let reaction stop its subprocesses before killing them 2026-02-12 12:00:00 +01:00
crates/treedb Separate treedb into its own crate 2025-12-14 12:00:00 +01:00
logo NLnet statement 2025-06-01 12:00:00 +02:00
packaging systemd: Let reaction stop its subprocesses before killing them 2026-02-12 12:00:00 +01:00
plugins reaction-plugin: Add metadata 2026-02-12 12:00:00 +01:00
src Set CapabiltyBoundingSet again 2026-02-12 12:00:00 +01:00
tests Set CapabiltyBoundingSet again 2026-02-12 12:00:00 +01:00
.ccls test infrastructure, new conf's state_directory, less deps 2024-11-13 12:00:00 +01:00
.envrc reaction-plugin-ipset: first work session 2026-02-09 12:00:00 +01:00
.gitignore .gitignore cleanup 2026-02-12 12:00:00 +01:00
.gitlab-ci.yml 📦👷 — Build better deb package in release.sh + test building in CI 2024-04-03 16:16:53 +02:00
ARCHITECTURE.md Update ARCHITECTURE.md 2026-02-12 12:00:00 +01:00
build.rs Removal of nft46 and ip46tables 2026-02-11 12:00:00 +01:00
Cargo.lock release v2.3.0 2026-02-11 12:00:00 +01:00
Cargo.toml release v2.3.0 2026-02-11 12:00:00 +01:00
Dockerfile release v2.3.0 2026-02-11 12:00:00 +01:00
LICENSE Add AGPL LICENSE 2023-04-11 11:03:50 +00:00
Makefile Removal of nft46 and ip46tables 2026-02-11 12:00:00 +01:00
README.md Removal of nft46 and ip46tables 2026-02-11 12:00:00 +01:00
release.py release v2.3.0 2026-02-11 12:00:00 +01:00
shell.nix ipset: so much ~~waow~~ code 2026-02-09 12:00:00 +01:00
TODO Better plugin process management 2026-02-09 12:00:00 +01:00

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 yet. However, it already works well on many 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 faster 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 😉

🇬🇧 in-depth blog article / 🇫🇷 french version

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 (more powerful) are supported. both are extensions of JSON, so JSON is transitively supported.

  • See reaction.yml or reaction.jsonnet for a fully explained reference (ipv4 + ipv6)
  • See the wiki for multiple examples, security recommendations and FAQ.
  • See server.jsonnet for a real-world configuration
  • See 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 part of the wiki before starting 🆙):
/etc/reaction.yml
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=<ip>'
          - 'Failed password for .* from <ip>'
          - 'Invalid user .* from <ip>'
          - 'banner exchange: Connection from <ip> port [0-9]*: invalid format'
        retry: 3
        retryperiod: '6h'
        actions:
          ban:
            cmd: [ 'iptables', '-w', '-I', 'reaction', '1', '-s', '<ip>', '-j', 'DROP' ]
          unban:
            cmd: [ 'iptables', '-w', '-D', 'reaction', '1', '-s', '<ip>', '-j', 'DROP' ]
            after: '48h'
/etc/reaction.jsonnet
local banFor(time) = {
  ban: {
    cmd: ['iptables', '-w', '-A', 'reaction', '-s', '<ip>', '-j', 'DROP'],
  },
  unban: {
    cmd: ['iptables', '-w', '-D', 'reaction', '-s', '<ip>', '-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=<ip>',
            @'Failed password for .* from <ip>',
            @'banner exchange: Connection from <ip> port [0-9]*: invalid format',
            @'Invalid user .* from <ip>',
          ],
          retry: 3,
          retryperiod: '6h',
          actions: banFor('48h'),
        },
      },
    },
  },
}

It is recommended to setup reaction with nftables or ipset + iptables, which are much more performant solutions than iptables alone.

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.

old binaries

ip46tables and nft46 binaries are no longer part of reaction. If you really need them, see the last commit that included them.

Wiki

You'll find more ressources, service configurations, etc. on the wiki!

We recommend that you read the Good Practices chapters before starting.

Installation

Packaging status

Binaries

Executables and .deb packages are provided in the releases page, 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 has not yet been upstreamed.

OpenBSD

See the wiki.

Compilation

You'll need a recent rust toolchain for reaction and a c compiler for ip46tables.

$ make

Don't hesitate to take a look at the Makefile to understand what's happening!

Installation

To install the binaries

make install

To install the systemd file as well

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.

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 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. French version: #reaction-dev-fr:club1.fr.

Help

You can ask for help in the issues or in this Matrix room: #reaction-users-en:club1.fr. French version: #reaction-users-fr:club1.fr. You can alternatively send a mail: reaction on domain ppom.me.

Funding

This project is currenlty funded through the NGI0 Core Fund, a fund established by NLnet with financial support from the European Commission's Next Generation Internet programme.

NLnet logo