action_log: log to systemd, aka. enable recidive detection
parent
c5135e847a
commit
0140a934c2
|
@ -27,4 +27,4 @@ For more in-depth documentation, please refer to these pages:
|
|||
- [the `action_noop` module](doc/noop.md)
|
||||
- [the `action_email` module](doc/action_email.md)
|
||||
- [the `action_dailyReport` module](doc/action_dailyReport.md)
|
||||
- [the `action_nftBan` module](doc/action_nftBan.md)
|
||||
- [the `action_nftBan` and `action_log` modules](doc/logandban.md)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
The software requirements are:
|
||||
|
||||
* a modern systemd-based Linux operating system (eg. [Archlinux](https://archlinux.org/)- or [Fedora](https://getfedora.org/)-based distributions);
|
||||
* python, at least version 3.1 (or [more, depending on the modules](doc/intro_tech.md) being used);
|
||||
* python, at least version 3.4 (or [more, depending on the modules](intro_tech.md) being used);
|
||||
* [python-systemd](https://www.freedesktop.org/software/systemd/python-systemd/journal.html);
|
||||
* [nftables](http://wiki.nftables.org/) _if_ IP address bans are to be managed;
|
||||
* a sendmail-like program _if_ emails are wanted.
|
||||
|
|
|
@ -15,7 +15,7 @@ It should be noted, that modern Python API are used. Thus:
|
|||
* Python version ≥ 3.1 is required for managing modules (`importlib`);
|
||||
* Python version ≥ 3.1 is required for loading the configuration (json’s `object_pairs_hook`);
|
||||
* Python version ≥ 3.2 is required for the daily report and emails (string’s `format_map`);
|
||||
* Python version ≥ 3.4 is required for the daily report (`enum`);
|
||||
* Python version ≥ 3.4 is required for the daily report and logging, thus also the log action (`enum`);
|
||||
* Python version ≥ 3.5 is required for IP address bans and emails, thus also the daily report (subprocess’ `run`);
|
||||
* Python version ≥ 3.6 is required for emails, thus also the daily report (`headerregistry`, `EmailMessage`).
|
||||
|
||||
|
|
|
@ -1,4 +1,33 @@
|
|||
# Ban IP addresses after they misbehaved
|
||||
# Log entries creation, and ban of IP addresses
|
||||
|
||||
## Log entries
|
||||
|
||||
The main purpose of creating new log entries, is to detect recidives in bad behaviour: after an IP address misbehaves, it gets banned, and we generate a log line for that; such log lines get counted, and eventually trigger a harsher, recidive, ban of the same IP address. Several levels of bans can thus be stacked, up to an unlimited ban, if such is wanted.
|
||||
|
||||
Action `action_log` takes a mandatory `message` argument, which is a template for the message to be sent.
|
||||
Optionally, the log level can be changed from the default (which is “INFO”) by setting the `level` parameter; valid values are “EMERG”, “ALERT”, “CRIT”, “ERR”, “WARNING”, “NOTICE”, “INFO”, and “DEBUG” (see [Syslog severity levels](https://en.wikipedia.org/wiki/Syslog#Severity_level) for the definitions).
|
||||
|
||||
The `message` parameter is a Python [string format](https://docs.python.org/3/library/string.html#formatstrings).
|
||||
This means that any key in the current entry may be referrenced by its name between curly braces.
|
||||
This also means that literal curly braces must be doubled, lest they are read as the start of a template placeholder.
|
||||
|
||||
Here are some examples:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "action_log", "args": { "message": "Ban from SSH for {thatIP}." }
|
||||
}
|
||||
|
||||
{
|
||||
"action": "action_log",
|
||||
"args": {
|
||||
"level": "NOTICE",
|
||||
"message": "Recidive ban from SSH for {thatIP}."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Ban IP addresses after they misbehaved
|
||||
|
||||
Linux provides a number of firewall solutions: [iptables](http://www.netfilter.org/), its successor [nftables](http://wiki.nftables.org/), and many iptables frontends like [Shorewall](http://www.shorewall.net/) or RedHat’s [firewalld](http://www.firewalld.org/).
|
||||
For Pyruse, **nftables** was chosen, because it is modern and light-weight, and provides interesting features.
|
||||
|
@ -59,7 +88,7 @@ Here are examples:
|
|||
}
|
||||
```
|
||||
|
||||
## List the currently banned addresses
|
||||
### List the currently banned addresses
|
||||
|
||||
To see what IP addresses are currently banned, here is the `nft` command:
|
||||
|
||||
|
@ -85,7 +114,7 @@ table ip Inet4 {
|
|||
|
||||
_Note_: The un-rounded timeouts are post-reboot restored bans.
|
||||
|
||||
## Un-ban an IP address
|
||||
### Un-ban an IP address
|
||||
|
||||
It is bound to happen some day: you will want to un-ban a banned IP address.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# pyruse is intended as a replacement to both fail2ban and epylog
|
||||
# Copyright © 2017–2018 Y. Gablin
|
||||
# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing.
|
||||
import string
|
||||
from pyruse import base, log
|
||||
|
||||
class Action(base.Action):
|
||||
def __init__(self, args):
|
||||
super().__init__()
|
||||
self.level = log.Level[args.get("level", log.Level.INFO.name)]
|
||||
self.template = args["message"]
|
||||
values = {}
|
||||
for (_void, name, _void, _void) in string.Formatter().parse(self.template):
|
||||
if name:
|
||||
values[name] = None
|
||||
self.values = values
|
||||
|
||||
def act(self, entry):
|
||||
for (name, _void) in self.values.items():
|
||||
self.values[name] = entry.get(name, None)
|
||||
msg = self.template.format_map(self.values)
|
||||
log.log(self.level, msg)
|
|
@ -1,28 +1,28 @@
|
|||
# pyruse is intended as a replacement to both fail2ban and epylog
|
||||
# Copyright © 2017–2018 Y. Gablin
|
||||
# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing.
|
||||
from enum import Enum, unique
|
||||
from systemd import journal
|
||||
|
||||
EMERG = 0 # System is unusable.
|
||||
ALERT = 1 # Action must be taken immediately.
|
||||
CRIT = 2 # Critical conditions, such as hard device errors.
|
||||
ERR = 3 # Error conditions.
|
||||
WARNING = 4 # Warning conditions.
|
||||
NOTICE = 5 # Normal but significant conditions.
|
||||
INFO = 6 # Informational messages.
|
||||
DEBUG = 7
|
||||
@unique
|
||||
class Level(Enum):
|
||||
EMERG = 0 # System is unusable.
|
||||
ALERT = 1 # Action must be taken immediately.
|
||||
CRIT = 2 # Critical conditions, such as hard device errors.
|
||||
ERR = 3 # Error conditions.
|
||||
WARNING = 4 # Warning conditions.
|
||||
NOTICE = 5 # Normal but significant conditions.
|
||||
INFO = 6 # Informational messages.
|
||||
DEBUG = 7
|
||||
|
||||
def log(level, string):
|
||||
journal.send(string, PRIORITY = level)
|
||||
journal.send(string, PRIORITY = level.value)
|
||||
|
||||
def debug(string):
|
||||
global DEBUG
|
||||
log(DEBUG, string)
|
||||
log(Level.DEBUG, string)
|
||||
|
||||
def notice(string):
|
||||
global NOTICE
|
||||
log(NOTICE, string)
|
||||
log(Level.NOTICE, string)
|
||||
|
||||
def error(string):
|
||||
global ERR
|
||||
log(ERR, string)
|
||||
log(Level.ERR, string)
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# pyruse is intended as a replacement to both fail2ban and epylog
|
||||
# Copyright © 2017–2018 Y. Gablin
|
||||
# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing.
|
||||
from unittest.mock import patch
|
||||
from pyruse import log
|
||||
from pyruse.actions.action_log import Action
|
||||
|
||||
@patch('pyruse.actions.action_log.log.log')
|
||||
def whenLogThenRightSystemdCall(mockLog):
|
||||
for level in log.Level:
|
||||
Action({"level": level.name, "message": "Test: {text}"}).act({"text": "test message"})
|
||||
mockLog.assert_called_with(level, "Test: test message")
|
||||
|
||||
def unitTests():
|
||||
whenLogThenRightSystemdCall()
|
|
@ -20,7 +20,7 @@ def main():
|
|||
|
||||
# Unit tests
|
||||
import filter_equals, filter_greaterOrEquals, filter_in, filter_inNetworks, filter_lowerOrEquals, filter_pcre, filter_pcreAny, filter_userExists
|
||||
import action_counterRaise, action_counterReset, action_dailyReport, action_email, action_nftBan
|
||||
import action_counterRaise, action_counterReset, action_dailyReport, action_email, action_log, action_nftBan
|
||||
|
||||
filter_equals.unitTests()
|
||||
filter_greaterOrEquals.unitTests()
|
||||
|
@ -34,6 +34,7 @@ def main():
|
|||
action_counterReset.unitTests()
|
||||
action_dailyReport.unitTests()
|
||||
action_email.unitTests()
|
||||
action_log.unitTests()
|
||||
action_nftBan.unitTests()
|
||||
|
||||
# Integration test
|
||||
|
|
Loading…
Reference in New Issue