route add $BAD_IP gw 127.0.0.1 lo

Posted on Jan 2, 2022
tl;dr: New year, new homelab challenges! Blocking malicious traffic in my perimeter firewall using CrowdSec. In this guide I am using an Ubuntu VM, but it should work on other platforms as well, just read the CrowdSec docs.

Hey Victor,

I am using a firewall with no real compute power, so I cannot run a full fledged IDS/IPS on it.
Setting up Suricata or such is way too complicated. Yet I want to run my web servers and other services and be able to sleep at night without worrying about evil people. Can you help me identify malicious traffic and, if detected, block the source IP from my whole network?

  Hold my previously ignored attempted intrusion alerts, I’m going in!  

Background

I recently - well, actually quite some time ago, but just now started fiddling around with it - stumbled upon this nifty little tool called CrowdSec.

Upon googling, I couldn’t find anything covering this scenario already (well of course, as this is probably worst practice), so I figured I’ll have to do it myself.
As I already have some blocklists, for example from FireHOL automatically downloaded and imported to my MikroTik firewall(s) at set intervals, this shouldn’t be too hard to implement!

To take actions on CrowdSec’s already made decisions (block/challenge attackers), CrowdSec uses bouncers.

bouncers are standalone software pieces in charge of acting upon a decision taken by crowdsec : block an IP, present a captcha, enforce MFA on a given user, etc.

Cool! But I cannot find anything for MikroTik (or any other perimeter firewall).
Luckily, CrowdSec has support for a custom bouncer, which we can use!

crowdsec-custom-bouncer will periodically fetch new and expired/removed decisions from CrowdSec Local API and will pass them as arguments to a custom user script.

During some RTFM, we can see that The custom binary will be called by CrowdSec with the following arguments:

my_script.sh add <ip> <duration> <reason> <json_object> 
my_script.sh del <ip> <duration> <reason> <json_object>

As I hardly ever look at my firewall logs, I immediately realize that I can skip some of the arguments. I know that CrowdSec has a default ban time of 4 hours (which I’ll be extending to atleast 24 hours - bad guys, stay out!), and I trust CrowdSec’s decision making. That removes the need for the Duration and Reason arguments. The JSON Object is not used by RouterOS either, atleast not in my setup, so I will ignore that as well.

My script for generating the lists can be found in my GitHub Repository.

Setting up

  1. Install and configure CrowdSec as per the official docs
  2. Install the custom bouncer:
sudo apt install crowdsec-custom-bouncer
  1. We need a few things before configuring the custom bouncer:
    a. bin_path - this can, and will in my case, be a shell script.
    b. api_url - default is localhost:8080.
    c. api_key - Create your own by running cscli bouncers add <bouncer-name>

  2. Download (or use as inspiration) my script from the GitHub Repository and place it somewhere CrowdSec can run it. Don’t forget to make it executable (chmod +x).

  3. Configure the bouncer by editing the /etc/crowdsec/bouncers/crowdsec-custom-bouncer.yaml file.

bin_path: <absolute_path_to_script>
piddir: /var/run/
update_frequency: 10s
daemonize: true
log_mode: file
log_dir: /var/log/
log_level: info
api_url: <API_URL>
api_key: <API_KEY>
cache_retention_duration: 10s 
  1. Enable the bouncer
sudo systemctl enable crowdsec-custom-bouncer
  1. Set up your firewall to import the list

  2. All done! Good job!

Testing:

Using a HTTP brute-forcing tool such as Gobuster, we can see that we:

  1. Start out with no entries in the list.
  2. After running GoBuster, managed to lock us out pretty quick
  3. Have gotten our IP address added to the .rsc, which will then be imported by the MikroTik a few moments later.
  4. Can easily delete our blocked IP from CrowdSec and the blocklist using
cscli decisions delete -i <IP>

Proof!

And oh, we can also get alerts to Slack, by configuring the /etc/crowdsec/notifications/slack.yaml file with the Webhook URL, as well as uncommenting the

[...]
notifications:
  - slack_default
[...]

part of the /etc/crowdsec/profiles.yaml file.

That’s all!