Imagine a common scenario in which you have to manage multiple servers and all those servers send its logs to a remote centralized syslog server. In this scenario may that be even the final servers do not store its logs locally unless a network connection problem occurs.
So you have a centralized syslog server in which you store (among others) all detected break-in attempts, brute force attacks, etc. The next question is, once you have detected a break-in attempt or a brute force attack in one of your servers, how can you remotely ban the attacker IP in that server and in the other ones?
Fail2Ban is a great and very powerful tool, but it is designed to work locally, what turns it in a nightmare when trying to use it in our scenario. Even if the logs were stored locally, you still have to manage the fail2ban filters and actions in all servers, although this approach could be solved by using some kind of shared storage, etc.
Since I did not found any open source project to manage the given approach, I decided to write my own solution, here is where net2ban comes in rescue.
As you can guess, Net2Ban is inspired in fail2ban, but it is much more simple and flexible. It has two main parts: Server, and client. Aditionaly it uses RabbitMQ as message brooker to communicate from server to clients, and has a tool to send commands to the server in order to propagate the actions.
For each communication between nodes, net2ban uses AES256 encryption in CBC mode, PBKDF2 to generate the session key and HMAC-SHA256, in order to protect and guarantee the integrity,confidentiality and authenticity of each message. The parameters to set up the encryption schema will be explained in the next points.
To identify the difference between fail2ban and net2ban, and understand how they can be mixed, here is a simple schema representing the workflow of both tools:
Fail2Ban:
--> filters ->- 2 | | ############## 1 ######################## 3 ########### # LOG SOURCE # -----> # Fail2Ban (Detection) # -----> # Actions # ############## ######################## ###########
Net2Ban:
--> intelligence ->- 2 | | ######################## ############## 1 # DETECTION AND/OR # 3 ########### # LOG SOURCE # -----> # CORRELATION ENGINE # -----> # ACTIONS # --- ############## ######################## ########### | 4 | -------------------------------------------------------------------| | 4 | ############################### 5 ################### |--> # Net2Ban: DO 'X' ACTION WITH # -----> # RABBITMQ SERVER # -- # 'Y' IP AND 'Z' PORT(S) # ################### | ############################### | 6 | ------------------------------------------------------------------| | 6 | ############################ | # NET2BAN SERVER: FORWARDS # 7 ################################### |--> # THE MESSAGE TO THE NODES # -----> # NET2BAN CLIENT: DOES 'X' ACTION # # THROUGH RABBITMQ # # WITH 'Y' IP AND 'Z' PORT(S) # ############################ ###################################
It is true that there are many message brokers such as reddit or activemq, some of them give you a higher performance level when dealing in big environments such as WANs, but in my case, there was a RabbitMQ server already installed in the network. On the other hand, net2ban can be modified to work with a message broker specified in the configuration file. This approach could be made in future versions...
The following tasks are commons and required to install net2ban in the servers as well as in the clients:
apt-get install python-pika python-passlib python-crypto git clone https://github.com/sch3m4/net2ban.git net2ban cd net2ban mkdir -p /usr/share/net2ban/actions useradd -r -M net2ban -s /bin/false -b /usr/share cp -r net2ban /usr/share/net2ban/ cp net2ban.py /usr/share/net2ban/ cp net2ban.cfg /usr/share/net2ban/ cp net2ban_init.d /etc/init.d/net2ban chown root:root /etc/init.d/net2ban chmod 0750 /etc/init.d/net2ban update-rc.d net2ban defaults
Some extra actions to be made on server side:
apt-get install rabbitmq-server fail2ban service fail2ban stop cp net2ban.cfg /usr/share/net2ban/ cp net2banctl.py /sbin/net2banctl chown root:root /sbin/net2banctl chmod 0750 /sbin/net2banctl
Some extra actions to be made on client side:
apt-get install sudoers
In order to allow net2ban user execute the iptables binary, add the following entry to your "/etc/sudoers" file:
echo 'net2ban ALL=NOPASSWD: /sbin/iptables' >> /etc/sudoers
NOTE: Add an entry like the aforementioned for each privileged command susceptible of being executed by net2ban clients
As final steps, don't forget to change the owner and permissions of net2ban files:
chown -R net2ban:net2ban /usr/share/net2ban chmod -R 0750 /usr/share/net2ban
The net2ban.cfg looks as follows:
[global] server = 172.16.0.1 rounds = 12000 secret = 57bae6f17927fee0309cd0fcf900903d0bf49277 mode = server valid_wtime = 3600 authkey = fe64809ba0286c72123d15ffb3554f261ef835d0 queue_prefix = net2ban_[server] input = input_net2ban key = 1e3648858a30a105374c1a8a7f05e4fc0dff2abc
[keygen] keylen = 40
[client1] key = key1
The options in this sections are used by the server and clients. It can be used to set some parameters to a default value.
This is the host/ip of the RabbitMQ server, used to establish the communication between the server and clients.
This is the working mode of this instance of net2ban, and have to be set to "client" or "server".
Valid window time for the messages, in seconds. All those messages that have been sent before the relative "valid_wtime" will be discarted.
Rounds to be used when generating the session key by using PBKDF2
Shared secret to be used with the key of each client, to generate the session key by using PBKDF"
Authentication key to generate the HMAC-SHA256 of each message
Prefix to create a queue for each client
This section is specific to the server side.
Queue name to read the messages and forward them
Key used to decrypt the messages received
When generating random keys to be used as auth key/secret/key through net2banctl, this parameter set the length of the generated random string
The name of this section is arbitrary and should not match with other entries. The name of this section will be used as client's name.
Key used to decrypt the messages received
The above configuration schema is the minimum required, as it can be modified by adding some parameters to server and clients sections in order to override the default ones. On this point, you can add the following options to any section related to clients/server: ..* rounds ..* secret ..* key ..* valid_wtime ..* authkey
Referring to the cryptographic values, the only key that must be similar in server and client side, is the "secret" option. If you locally modify a key on client side, remember to set the same key in the section belonging to that client in net2ban.cfg on server side.
On this point we're going to generate a configuration file to use the RabbitMQ server on 172.16.15.2 using two clients: client1 and client2. Moreover we will use individual configurations for the clients.
At this step we are going to modify the net2ban.cfg file to set the proper values in order to work on server side, and afterwards copy the configuration to the clients.
The minimum required configuraton looks as follows:
[global] server = 172.16.15.2 rounds = 12000 secret = 57bae6f17927fee0309cd0fcf900903d0bf49277 mode = server valid_wtime = 3600 authkey = fe64809ba0286c72123d15ffb3554f261ef835d0 queue_prefix = net2ban_[server] input = input_net2ban key = 1e3648858a30a105374c1a8a7f05e4fc0dff2abc
[keygen] keylen = 40
[client1] key = key1
[client2] key = key2
To generate the client keys, we will use net2banctl:
$ for i in $(seq 1 2); do net2banctl gen_random ; done ca82cd669623f6434e02eecade0236b50fc18cb3 767c138a4483e692f41af3d1491f06c0ead41b5b $
Now, set the 'key' clients option to these values:
[client1] key = ca82cd669623f6434e02eecade0236b50fc18cb3[client2] key = 767c138a4483e692f41af3d1491f06c0ead41b5b
If you want some client to use a different value of "rounds", just specify it in his section:
[client2] rounds = 100 key = 767c138a4483e692f41af3d1491f06c0ead41b5b
Indeed the client configuration is almost done. On this step you still need access to the server.
To verify that net2banctl can see the configuration file and the clients specified on it, execute:
$ net2banctl list_clients client1 client2
Now, we need to export the configuration of each client and save it in /usr/share/net2ban/net2ban.cfg on each client. To do that, execute the following on the server:
$ net2banctl get_client client1 > client1.cfg $ cat client1.cfg [global] server = 172.16.15.2 rounds = 12000 secret = 57bae6f17927fee0309cd0fcf900903d0bf49277 mode = client valid_wtime = 3600 authkey = fe64809ba0286c72123d15ffb3554f261ef835d0 queue_prefix = net2ban_[keygen] keylen = 40
[client1] key = ca82cd669623f6434e02eecade0236b50fc18cb3 $
In order to use net2ban in conjunction with fail2ban, you need to do some changes to your fail2ban configuration. First of all, copy your fail2ban action file (defined in /etc/fail2ban/jail.conf, [DEFAULT]/banaction) to /usr/share/net2ban/actions. In my case, I'm using "iptables-multiport":
$ cp /etc/fail2ban/actions.d/iptables-multiport.conf /usr/share/net2ban/actions/
Edit the original fail2ban action file (in my case: /etc/fail2ban/actions.d/iptables-multiport.conf) and modify the value of the defined actions to the following ones:
[Definition] actionstart = net2banctl exec iptables-multiport actionstart name=<name> chain=<chain> protocol=<protocol> port=<port> actionstop = net2banctl exec iptables-multiport actionstop name=<name> chain=<chain> protocol=<protocol> port=<port> actionban = net2banctl exec iptables-multiport actionban name=<name> ip=<ip> actionunban = net2banctl exec iptables-multiport actionunban name=<name> ip=<ip> # cheats fail2ban actioncheck = echo -n "<chain><name>" && exit 0[Init] name = default port = ssh protocol = tcp chain = INPUT
We're almost done. The final step is to start the net2ban daemon in server and clients side and send the "actions" file to the clients:
$ for i in $(net2banctl list_clients); do net2banctl update $i /usr/share/net2ban/actions/iptables-multiport.conf ; done $
IMPORTANT: Remember to modify the actions file and add "sudo" before "iptables" to execute each command as root.
Personally I prefer to ban the IP without taking care about the port, so the given IP is globally banned, here is my "iptables-multiport.conf" actions file:
[Definition] actionstart = sudo iptables -N fail2ban-<name> sudo iptables -A fail2ban-<name> -j RETURN sudo iptables -I <chain> -p <protocol> -j fail2ban-<name> actionstop = sudo iptables -D <chain> -p <protocol> -j fail2ban-<name> sudo iptables -F fail2ban-<name> sudo iptables -X fail2ban-<name> actionban = sudo iptables -I fail2ban-<name> 1 -s <ip> -j DROP actionunban = sudo iptables -D fail2ban-<name> -s <ip> -j DROP [Init] name = default port = ssh protocol = tcp chain = INPUT
After that you should see something like this in your syslog:
........ net2ban: File "iptables-multiport" updated
net2ban is not only restricted to be used with fail2ban, due to its architecture it can be used with any software. Imagine the following scenario:
..* User 'user1' log in into a corporate application ..* User 'user1' has not loged in his workstation
The correlation engine detects an abnormal behaviour and executes net2banctl to automatically ban the IP on the other servers and/or applications (keep in mind that you are not limited to execute the iptables binary on client side) and report it.
By default, net2ban uses syslog to send logs, so if you want to save the logs generated by the tool in a different file (by default is logs to "daemon" file) you can modify your syslog-ng rules and add the following lines:
destination d_net2ban { file ( "/var/log/net2ban.log" ); }; destination d_rnet2ban { file ( "/var/log/remote/$FULLHOSTNAME/net2ban.log" ); filter f_net2ban { facility ( daemon ); program ( "net2ban" ); }; log { source(s_src); filter(f_net2ban); destination(d_net2ban); flags ( final ); }; log { source(s_udp); filter(f_net2ban); destination(d_rnet2ban); flags ( final ); };
Keep in mind that the "log" rules must be the first matching rules in order to avoid duplicate logs.
Whenever fail2ban detects a break-in attept, it will generate an entry in the log like this:
.... fail2ban.actions: WARNING [dovecot] Ban 95.48.84.XXX
That action will execute net2banctl and send the message to the server, and the following log entry will be generated in your server:
..... net2ban: Forwarding: exec/iptables-multiport
Finally, the net2ban nodes will receive the message, send a log entry and execute the action:
..... net2ban: IP 95.48.84.XXX banned
$ iptables -nvL | grep -i 95.48.84.XXX 9 564 DROP all -- * * 95.48.84.XXX 0.0.0.0/0 $
net2ban uses persistent queues on RabbitMQ, if a client is not connected and the server sends five messages to that client, the messages are stored in the queue, waiting for the client to connect and read them. On that point is very important the parameter "valid_wtime" in order to discard old messages. Even in a normal behaviour, and due to the parameter "valid_wtime" is very important that all nodes to be synchronized (ex: by using NTP)
The code is very dirty, it's a program written very fast "to work right now"...