User Tools

Site Tools


fail2bannotifications

Fail2ban: Set up E-Mail and/or Telegram messenger smtp.py-notifications with Tux

Hey, my name is "Tux"! Did you ever wonder how you can set up Fail2ban on Linux with E-Mail and/or Telegram messenger notification?

Hey, my name is "Tux" and this tutorial will show you how to set up Fail2ban so it can notify you about banning events by E-Mail and/or your personal and specific Telegram messenger channel by using Fail2ban's smtp.py pre-defined Python script.



First thoughts

Note

There are already a lot of good tutorials on the internet of how to install and configure Fail2ban on a Linux system so I won't get into this. This tutorial is all about including the built-in notification tool smtp.py into your Fail2ban configuration file and make it work.

Note

This tutorial is based on:
  • Linux server with SSH service enabled
  • Fail2ban version 0.11.1 package
    You can check your current installed version by executing the following command:

    $ fail2ban-server -V
    $ fail2ban-client -V

In addition, for E-Mail and/or Telegram notifications, it is assumed you've already set up a working E-Mail client and/or the specific smtp-to-telegram docker application. Optionally, to make smtp-to-telegram run on a Synology NAS with Docker package capability, you can also refer to this tutorial.


Start of tutorial

As you are aware of the information boxes above we move to practice now.

Let's begin!

Step 1: Configuring the jail file for smtp.py-notifications

Calling the Fail2ban's included smtp.py Python script with the according parameters as an action from your Fail2ban jail-file is really straight forward.

Just for your information: It might be helpful to take a look on the available defined parameters on the Parameters-section inside the smtp.py-script (Note that this is just an outtake, the whole script has a lot more content, of course):

Parameters
----------
jail : Jail
         The jail which the action belongs to.
name : str
        Named assigned to the action.
host : str, optional
        SMTP host, of host:port format. Default host "localhost" and
        port "25"
user : str, optional
        Username used for authentication with SMTP server.
password : str, optional
        Password used for authentication with SMTP server.
sendername : str, optional
        Name to use for from address in email. Default "Fail2Ban".
sender : str, optional
        Email address to use for from address in email.
        Default "fail2ban".
dest : str, optional
        Email addresses of intended recipient(s) in comma space ", "
        delimited format. Default "root".
matches : str, optional
        Type of matches to be included from ban in email. Can be one
        of "matches", "ipmatches" or "ipjailmatches". Default None
        (see man jail.conf.5).

As you can see on the script outtake above that there are only two parameters required when executing the smtp.py-action, all other parameters are optional (Note that the required parameters jail and name are being transferred automatically, you don't have to add them when calling the smtp.py-action. But you can override them if you need to.). But be aware, that those parameters with pre-defined default values actually do(!) send their default value by default when executing the smtp.py-action!

For example: For Telegram notifications it is just enough to just call the pre-defined smtp.py-action like this, all other parameters are not necessary to be set (Of course for E-Mail notification with user authentication you would have to set more parameters, in this case just do as your SMTP server requires!):

smtp.py[host="<mySmtpToTelegramHostIPAddress>:<mySmtpToTelegramHostPort>", sender="", dest="foo@bar.com"]

Note that the parameter sender has to be included with an empty string in case you are configuring smtp-to-telegram notifications because if you leave it unset it takes the default value fail2ban which did not work in my case (I've had to learn it the hard way!).
The destination content on the other hand really does not matter when using the smtp-to-telegram service, you can just put in foo@bar.com like I did, if you like.).

Here is an example of a running and fully working jail.local-file:

[DEFAULT]
ignoreip = 127.0.0.1/8
bantime  = 10m
findtime  = 10m
maxretry = 5
banaction = iptables-multiport

[sshd]
enabled = true
backend = systemd
filter = sshd
action = iptables[name=SSH, port=ssh, protocol=tcp]
        smtp.py[host="172.16.0.2:2500", sender="", dest="foo@bar.com"]
logpath = /var/log/secure
bantime = -1
findtime = 300
maxretry = 3

You can see on the jail.local-content above that there are the folowing two actions being called:

action = iptables[name=SSH, port=ssh, protocol=tcp]
        smtp.py[host="172.16.0.2:2500", sender="", dest="foo@bar.com"]

Please note: It is important to know that Fail2ban jail-files are not very tolerant when it comes into whitespaces and tabulators. In my case, directly after the first action iptables[name=SSH, port=ssh, protocol=tcp], I had to add a page break followed by a single tabulator. Please do not add any whitespaces and don't copy-paste from this website! Type it all down by your own to make sure it is being formatted correctly!

Also note that Fail2ban does take the SSH port-number defined inside the /etc/ssh/sshd_config-file. So if you have changed the default port number from 22 to whatever number, Fail2ban will take the correct port number so you don't have to worry about that.

Also note that you can repeat the smtp.py-action as many times as you want in case you want to have multiple notification channels (for example Telegram and E-Mail).

Now, after configuring everything similar as showed above please restart the Fail2ban service:

$ systemctl restart fail2ban

The tutorial should be finished here. Just check it by doing some SSH failure attempts (according to your jail-file configuration) manually to provoke an SSH-ban. You should get notified on your personal Telegram or E-Mail channel.

If not, you have either misconfigured your smtp-to-telegram docker service or -like in my case- you have a Linux system with enabled SELinux service set to enforced so you will not get any notifications yet as SELinux will prevent the Python script from being executed by default. In this case, please follow the tutorial below.


Step 2: Configuring SELinux for allowing smtp.py-notifications

After everything has been set up as described above, let's get to the tricky part now. First, edit the following file:

$ vi /etc/selinux/config

Now change the following line

SELINUX=enforcing

to

SELINUX=permissive

After the next system reboot SELinux is will be set to permissive mode which is required to capture the events preventing the smtp.py-script from being executed. So let's now reboot the system:

$ reboot now

After reboot SELinux is now in permissive mode. Check this by executing:

$ getenforce

Which should give you the following output:
Permissive

You will note soon that Telegram and/or E-Mail notifications should now work as SELinux is not in blocking mode anymore for now. But you don't want SELinux being disabled permanently as this takes down your system's security! So just keep following the tutorial! Login again with user root via SSH. Now make sure to provoke a few Fail2ban SSH-bans so the smtp.py-script blocking events will be captured by SELinux logs. After that (as already mentioned at least you now should have got new Telegram and/or E-Mail notifications), check the logs if it got blocked some smtp.py-script dependencies:

$ cat /var/log/audit/audit.log | grep fail2ban_t

If you see something like…

type=AVC msg=audit(1598509657.351:59): avc: denied { search } for pid=1175 comm="f2b/server" name="__pycache__" dev="dm-0" ino=12590952 scontext=system_u:system_r:fail2ban_t:s0 tcontext=system_u:object_r:unlabeled_t:s0 tclass=dir permissive=1

type=AVC msg=audit(1598509657.351:59): avc: denied { read } for pid=1175 comm="f2b/server" name="smtp.cpython-36.pyc" dev="dm-0" ino=12590953 scontext=system_u:system_r:fail2ban_t:s0 tcontext=system_u:object_r:unlabeled_t:s0 tclass=file permissive=1

type=AVC msg=audit(1598509657.351:59): avc: denied { open } for pid=1175 comm="f2b/server" path="/etc/fail2ban/action.d/__pycache__/smtp.cpython-36.pyc" dev="dm-0" ino=12590953 scontext=system_u:system_r:fail2ban_t:s0 tcontext=system_u:object_r:unlabeled_t:s0 tclass=file permissive=1

type=AVC msg=audit(1598509657.353:60): avc: denied { getattr } for pid=1175 comm="f2b/server" path="/etc/fail2ban/action.d/__pycache__/smtp.cpython-36.pyc" dev="dm-0" ino=12590953 scontext=system_u:system_r:fail2ban_t:s0 tcontext=system_u:object_r:unlabeled_t:s0 tclass=file permissive=1

type=AVC msg=audit(1598509657.873:61): avc: denied { name_connect } for pid=1175 comm="f2b/a.sshd" dest=2500 scontext=system_u:system_r:fail2ban_t:s0 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket permissive=1

type=AVC msg=audit(1598509705.324:115): avc: denied { name_connect } for pid=1175 comm="f2b/a.sshd" dest=2500 scontext=system_u:system_r:fail2ban_t:s0 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket permissive=1

type=AVC msg=audit(1598509705.998:120): avc: denied { search } for pid=1652 comm="f2b/server" name="__pycache__" dev="dm-0" ino=12590952 scontext=system_u:system_r:fail2ban_t:s0 tcontext=system_u:object_r:unlabeled_t:s0 tclass=dir permissive=1

type=AVC msg=audit(1598509705.998:120): avc: denied { read } for pid=1652 comm="f2b/server" name="smtp.cpython-36.pyc" dev="dm-0" ino=12590953 scontext=system_u:system_r:fail2ban_t:s0 tcontext=system_u:object_r:unlabeled_t:s0 tclass=file permissive=1

type=AVC msg=audit(1598509705.998:120): avc: denied { open } for pid=1652 comm="f2b/server" path="/etc/fail2ban/action.d/__pycache__/smtp.cpython-36.pyc" dev="dm-0" ino=12590953 scontext=system_u:system_r:fail2ban_t:s0 tcontext=system_u:object_r:unlabeled_t:s0 tclass=file permissive=1

type=AVC msg=audit(1598509705.998:121): avc: denied { getattr } for pid=1652 comm="f2b/server" path="/etc/fail2ban/action.d/__pycache__/smtp.cpython-36.pyc" dev="dm-0" ino=12590953 scontext=system_u:system_r:fail2ban_t:s0 tcontext=system_u:object_r:unlabeled_t:s0 tclass=file permissive=1

type=AVC msg=audit(1598509706.147:122): avc: denied { name_connect } for pid=1652 comm="f2b/a.sshd" dest=2500 scontext=system_u:system_r:fail2ban_t:s0 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket permissive=1

…it is clear that SELinux is preventing the smtp.py-script from being executed successfully. If so, let's create our own SELinux policy package. First, let's create the following directory:

$ mkdir /root/selinux

Navigate into it:

$ cd /root/selinux

Now let's create our own SELinux policy package inside the current directory:

$ grep "fail2ban_t" /var/log/audit/audit.log | audit2allow -M fail2ban-smtp-py

Now let's load/activate the custom SELinux module:

$ semodule -i /root/selinux/fail2ban-smtp-py.pp

Finally turn SELinux back to enforcing mode:

$ vi /etc/selinux/config

Chage the following line

SELINUX=permissive

to

SELINUX=enforcing

Now reboot the system:

$ reboot now

After reboot SELinux is now set back to enforcing mode. Check this by executing:

$ getenforce

Which should give you the following output:
Enforcing

Telegram and/or E-Mail notification should work now. Note that you can always unload/disable the custom SELinux module by executing:

$ semodule -r fail2ban-smtp-py


End of tutorial

fail2bannotifications.txt · Last modified: 2023/04/22