new doc, new examples, support -help
This commit is contained in:
@ -3,11 +3,8 @@
|
||||
// JSONNET is a superset of JSON, so one can write plain JSON files if wanted.
|
||||
// Note that YAML is also supported, see ./example.yml
|
||||
|
||||
// A JSONNET function
|
||||
// JSONNET functions
|
||||
local iptables(args) = ['ip46tables', '-w'] + args;
|
||||
// variables defined for later use.
|
||||
local iptablesban = iptables(['-A', 'reaction', '1', '-s', '<ip>', '-j', 'drop']);
|
||||
local iptablesunban = iptables(['-D', 'reaction', '1', '-s', '<ip>', '-j', 'drop']);
|
||||
// ip46tables is a minimal C program (only POSIX dependencies) present as a subdirectory.
|
||||
// it permits to handle both ipv4/iptables and ipv6/ip6tables commands
|
||||
|
||||
@ -48,7 +45,7 @@ local iptablesunban = iptables(['-D', 'reaction', '1', '-s', '<ip>', '-j', 'drop
|
||||
],
|
||||
|
||||
// streams are commands
|
||||
// they're run and their ouptut is captured
|
||||
// they are run and their ouptut is captured
|
||||
// *example:* `tail -f /var/log/nginx/access.log`
|
||||
// their output will be used by one or more filters
|
||||
streams: {
|
||||
@ -77,11 +74,10 @@ local iptablesunban = iptables(['-D', 'reaction', '1', '-s', '<ip>', '-j', 'drop
|
||||
actions: {
|
||||
// actions have a user-defined name
|
||||
ban: {
|
||||
// JSONNET substitutes the variable (defined at the beginning of the file)
|
||||
cmd: iptablesban,
|
||||
cmd: iptables(['-A', 'reaction', '-s', '<ip>', '-j', 'reaction-log-refuse']),
|
||||
},
|
||||
unban: {
|
||||
cmd: iptablesunban,
|
||||
cmd: iptables(['-D', 'reaction', '-s', '<ip>', '-j', 'reaction-log-refuse']),
|
||||
// if after is defined, the action will not take place immediately, but after a specified duration
|
||||
// same format as retryperiod
|
||||
after: '48h',
|
||||
@ -90,7 +86,7 @@ local iptablesunban = iptables(['-D', 'reaction', '1', '-s', '<ip>', '-j', 'drop
|
||||
onexit: true,
|
||||
// (defaults to false)
|
||||
// here it is not useful because we will flush the chain containing the bans anyway
|
||||
// (see /conf/reaction.service)
|
||||
// (with the stop commands)
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -9,7 +9,7 @@ patterns:
|
||||
|
||||
streams:
|
||||
tailDown1:
|
||||
cmd: [ "sh", "-c", "sleep 2; seq 100010 | while read i; do echo found $(($i % 100)); done" ]
|
||||
cmd: [ 'sh', '-c', 'sleep 2; seq 100010 | while read i; do echo found $(($i % 100)); done' ]
|
||||
filters:
|
||||
findIP:
|
||||
regex:
|
||||
@ -18,13 +18,13 @@ streams:
|
||||
retryperiod: 1m
|
||||
actions:
|
||||
damn:
|
||||
cmd: [ "echo", "<num>" ]
|
||||
cmd: [ 'echo', '<num>' ]
|
||||
undamn:
|
||||
cmd: [ "echo", "undamn", "<num>" ]
|
||||
cmd: [ 'echo', 'undamn', '<num>' ]
|
||||
after: 1m
|
||||
onexit: false
|
||||
tailDown2:
|
||||
cmd: [ "sh", "-c", "sleep 2; seq 100010 | while read i; do echo prout $(($i % 100)); done" ]
|
||||
cmd: [ 'sh', '-c', 'sleep 2; seq 100010 | while read i; do echo prout $(($i % 100)); done' ]
|
||||
filters:
|
||||
findIP:
|
||||
regex:
|
||||
@ -33,13 +33,13 @@ streams:
|
||||
retryperiod: 1m
|
||||
actions:
|
||||
damn:
|
||||
cmd: [ "echo", "<num>" ]
|
||||
cmd: [ 'echo', '<num>' ]
|
||||
undamn:
|
||||
cmd: [ "echo", "undamn", "<num>" ]
|
||||
cmd: [ 'echo', 'undamn', '<num>' ]
|
||||
after: 1m
|
||||
onexit: false
|
||||
tailDown3:
|
||||
cmd: [ "sh", "-c", "sleep 2; seq 100010 | while read i; do echo nanana $(($i % 100)); done" ]
|
||||
cmd: [ 'sh', '-c', 'sleep 2; seq 100010 | while read i; do echo nanana $(($i % 100)); done' ]
|
||||
filters:
|
||||
findIP:
|
||||
regex:
|
||||
@ -48,13 +48,13 @@ streams:
|
||||
retryperiod: 2m
|
||||
actions:
|
||||
damn:
|
||||
cmd: [ "true" ]
|
||||
cmd: [ 'true' ]
|
||||
undamn:
|
||||
cmd: [ "true" ]
|
||||
cmd: [ 'true' ]
|
||||
after: 1m
|
||||
onexit: false
|
||||
tailDown4:
|
||||
cmd: [ "sh", "-c", "sleep 2; seq 100010 | while read i; do echo nanana $(($i % 100)); done" ]
|
||||
cmd: [ 'sh', '-c', 'sleep 2; seq 100010 | while read i; do echo nanana $(($i % 100)); done' ]
|
||||
filters:
|
||||
findIP:
|
||||
regex:
|
||||
@ -63,8 +63,8 @@ streams:
|
||||
retryperiod: 2m
|
||||
actions:
|
||||
damn:
|
||||
cmd: [ "echo", "<num>" ]
|
||||
cmd: [ 'echo', '<num>' ]
|
||||
undamn:
|
||||
cmd: [ "echo", "undamn", "<num>" ]
|
||||
cmd: [ 'echo', 'undamn', '<num>' ]
|
||||
after: 1m
|
||||
onexit: false
|
||||
|
@ -4,7 +4,7 @@ WantedBy=multi-user.target
|
||||
|
||||
# See `man systemd.exec` and `man systemd.service` for most options below
|
||||
[Service]
|
||||
ExecStart=/path/to/reaction -c /etc/reaction.yml
|
||||
ExecStart=/path/to/reaction start -c /etc/reaction.yml
|
||||
|
||||
# Ask systemd to create /var/lib/reaction (/var/lib/ is implicit)
|
||||
StateDirectory=reaction
|
||||
|
147
config/server.jsonnet
Normal file
147
config/server.jsonnet
Normal file
@ -0,0 +1,147 @@
|
||||
// This is the extensive configuration used on a **real** server!
|
||||
|
||||
local banFor(time) = {
|
||||
ban: {
|
||||
cmd: ['ip46tables', '-w', '-A', 'reaction', '-s', '<ip>', '-j', 'reaction-log-refuse'],
|
||||
},
|
||||
unban: {
|
||||
after: time,
|
||||
cmd: ['ip46tables', '-w', '-D', 'reaction', '-s', '<ip>', '-j', 'reaction-log-refuse'],
|
||||
},
|
||||
};
|
||||
|
||||
{
|
||||
patterns: {
|
||||
// IPs can be IPv4 or IPv6
|
||||
// ip46tables (C program also in this repo) handles running the good commands
|
||||
ip: {
|
||||
regex: @'(([0-9]{1,3}\.){3}[0-9]{1,3})|([0-9a-fA-F:]{2,90})',
|
||||
},
|
||||
},
|
||||
|
||||
streams: {
|
||||
// Ban hosts failing to connect via ssh
|
||||
ssh: {
|
||||
cmd: [' journalctl', '-fn0', '-u', 'sshd.service'],
|
||||
filters: {
|
||||
failedlogin: {
|
||||
regex: [
|
||||
@'authentication failure;.*rhost=<ip>',
|
||||
@'Connection reset by authenticating user .* <ip>',
|
||||
],
|
||||
retry: 3,
|
||||
retryperiod: '6h',
|
||||
actions: banFor('48h'),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Ban hosts which knock on closed ports.
|
||||
// It needs this iptables chain to be used to drop packets:
|
||||
// ip46tables -N log-refuse
|
||||
// ip46tables -A log-refuse -p tcp --syn -j LOG --log-level info --log-prefix 'refused connection: '
|
||||
// ip46tables -A log-refuse -m pkttype ! --pkt-type unicast -j nixos-fw-refuse
|
||||
// ip46tables -A log-refuse -j DROP
|
||||
kernel: {
|
||||
cmd: ['journalctl', '-fn0', '-k'],
|
||||
filters: {
|
||||
portscan: {
|
||||
regex: ['refused connection: .*SRC=<ip>'],
|
||||
retry: 4,
|
||||
retryperiod: '1h',
|
||||
actions: banFor('720h'),
|
||||
},
|
||||
},
|
||||
},
|
||||
// Note: nextcloud and vaultwarden could also be filters on the nginx stream
|
||||
// I did use their own logs instead because it's less logs to parse than the front webserver
|
||||
|
||||
// Ban hosts failing to connect to Nextcloud
|
||||
nextcloud: {
|
||||
cmd: ['journalctl', '-fn0', '-u', 'phpfpm-nextcloud.service'],
|
||||
filters: {
|
||||
failedLogin: {
|
||||
regex: [
|
||||
@'"remoteAddr":"<ip>".*"message":"Login failed:',
|
||||
@'"remoteAddr":"<ip>".*"message":"Trusted domain error.',
|
||||
],
|
||||
retry: 3,
|
||||
retryperiod: '1h',
|
||||
actions: banFor('1h'),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Ban hosts failing to connect to vaultwarden
|
||||
vaultwarden: {
|
||||
cmd: ['journalctl', '-fn0', '-u', 'vaultwarden.service'],
|
||||
filters: {
|
||||
failedlogin: {
|
||||
actions: banFor('2h'),
|
||||
regex: [@'Username or password is incorrect\. Try again\. IP: <ip>\. Username:'],
|
||||
retry: 3,
|
||||
retryperiod: '1h',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Used with this nginx log configuration:
|
||||
// log_format withhost '$remote_addr - $remote_user [$time_local] $host "$request" $status $bytes_sent "$http_referer" "$http_user_agent"';
|
||||
// access_log /var/log/nginx/access.log withhost;
|
||||
nginx: {
|
||||
cmd: ['tail', '-n0', '-f', '/var/log/nginx/access.log'],
|
||||
filters: {
|
||||
// Ban hosts failing to connect to Directus
|
||||
directus: {
|
||||
regex: [
|
||||
@'^<ip> .* "POST /auth/login HTTP/..." 401 [0-9]+ .https://directusdomain',
|
||||
],
|
||||
retry: 6,
|
||||
retryperiod: '4h',
|
||||
actions: banFor('4h'),
|
||||
},
|
||||
|
||||
// Ban hosts presenting themselves as bots of ChatGPT
|
||||
gptbot: {
|
||||
regex: [@'^<ip>.*GPTBot/1.0'],
|
||||
action: banFor('720h'),
|
||||
},
|
||||
|
||||
// Ban hosts failing to connect to slskd
|
||||
slskd: {
|
||||
regex: [
|
||||
@'^<ip> .* "POST /api/v0/session HTTP/..." 401 [0-9]+ .https://slskddomain',
|
||||
],
|
||||
retry: 3,
|
||||
retryperiod: '1h',
|
||||
actions: banFor('6h'),
|
||||
},
|
||||
|
||||
// Ban suspect HTTP requests
|
||||
// Those are frequent malicious requests I got from bots
|
||||
// Make sure you don't have honnest use cases for those requests, or your clients may be banned for 2 weeks!
|
||||
suspectRequests: {
|
||||
regex: [
|
||||
// (?:[^/" ]*/)* is a "non-capturing group" regex that allow for subpath(s)
|
||||
// example: /code/.env should be matched as well as /.env
|
||||
// ^^^^^
|
||||
@'^<ip>.*"GET /(?:[^/" ]*/)*\.env ',
|
||||
@'^<ip>.*"GET /(?:[^/" ]*/)*info\.php ',
|
||||
@'^<ip>.*"GET /(?:[^/" ]*/)*owa/auth/logon.aspx ',
|
||||
@'^<ip>.*"GET /(?:[^/" ]*/)*auth.html ',
|
||||
@'^<ip>.*"GET /(?:[^/" ]*/)*auth1.html ',
|
||||
@'^<ip>.*"GET /(?:[^/" ]*/)*password.txt ',
|
||||
@'^<ip>.*"GET /(?:[^/" ]*/)*passwords.txt ',
|
||||
@'^<ip>.*"GET /(?:[^/" ]*/)*dns-query ',
|
||||
// Do not include this if you have a Wordpress website ;)
|
||||
@'^<ip>.*"GET /(?:[^/" ]*/)*wp-login\.php',
|
||||
@'^<ip>.*"GET /(?:[^/" ]*/)*wp-includes',
|
||||
// Do not include this if a client must retrieve a config.json file ;)
|
||||
@'^<ip>.*"GET /(?:[^/" ]*/)*config\.json ',
|
||||
],
|
||||
action: banFor('720h'),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
@ -39,7 +39,6 @@
|
||||
},
|
||||
tailDown2: {
|
||||
cmd: ['sh', '-c', 'echo coucou; sleep 2m'],
|
||||
// cmd: ['sh', '-c', "echo 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 6 7 8 9 | tr ' ' '\n' | while read i; do sleep 3; echo found $(($i % 60)); done"],
|
||||
filters: {
|
||||
findIP: {
|
||||
regex: ['^found <num>$'],
|
||||
|
Reference in New Issue
Block a user