Do-it-Yourself Cloudflare on a Budget

by aestetix

As the American political landscape gets ever more heated and divisive, many tech companies are throwing their hat in the ring and treating customers differently based on their political views.  I think this is an incredibly stupid move for a company, regardless of whether or not I agree with the company's politics, because I believe companies should only be focusing on their products.  It makes me ask questions like: "Could a company ever drop my account over politics?"  Because such questions scare me, I often seek to figure out ways to replicate what the company does on my own and, if possible, for free.  In this article we'll explore one such example that could also wind up saving you some cash in the long term.

A well known company that has recently made decisions based on political views is Cloudflare.  Their technical offerings are two-fold: they offer website caching via a global content distribution network that can make your website much faster, and they offer protection against Distributed Denial-of-Service (DDoS) attacks.  While the caching offering is useful especially if you want to boost your site in search results, the DDoS protection is actually pretty easy to replicate on your own, provided you don't need deep packet inspection performed, which most sites don't.  When you add to this the fact that all of your DNS records must be hosted with Cloudflare to use their service, a less intrusive alternative that can handle some forms of DDoS mitigation for free sounds fairly appealing.

There are several kinds of DDoS attack.  While some aim at knocking a service or website completely offline, others are more focused.  A very common form of DDoS, and what we'll focus on here, comes not from trying to get the site shut down, but from attempting to brute-force a login without getting banned.  While setting a CAPTCHA for an IP after too many failed login attempts is helpful, it still allows traffic to reach your web servers and cause undue stress on your system's CPU.  It's far better to have a way to automatically cut off offending traffic before it even hits your web servers.

One of the best free tools to protect against a DDoS on Linux is called Fail2Ban.  This tool runs as a service, monitors your logs, and modifies your firewall according to the rules you set.  Therefore, you can configure it to protect against a simple DDoS in a few easy steps.  From this point on, I'm assuming you are running Ubuntu 18.04, but these steps can easily be translated to other Linux flavors.

Fail2Ban provides a number of security features out of the box, such as protection against SSH brute-force attacks.  To mitigate these, Fail2Ban scans the access log (usually /var/log/auth.log) and, if it sees an IP attempt unsuccessfully try to connect to the system via SSH (port 22) too many times, it will ban that IP in iptables for a set period of time.  But since our focus is web traffic, let's take a look at a log entry and convert it into a Fail2Ban rule.  For our example we are using the load-balancer HAProxy, but you could easily translate these steps to NGINX (or Apache).

First, make sure Fail2Ban is installed:

$ apt-get install fail2ban

Next, we look for an offending traffic pattern.  After a glance at the HAProxy log (/var/log/haproxy.log), we see a bunch of lines that look like this:

Oct 21 07:28:00 localhost haproxy[2342]: 192.168.0.1:1337 [21/0ct/2015:07:28:31.337] https_frontend wp_backend/wp 0/0/5/287/750 200 34854 - - ---- 384/384/213/0/0 0/0 "POST /wp-admin.php HTTP/1.1"

Log key:

Oct 21 07:28:00Log Time Stamp
localhostHostname or IP Address of HAProxy Host
haproxy[2342]:Process ID for the HAProxy Process
192.168.0.1:1337Source IP:Source Port
[21/0ct/2015:07:28:31.337]Timestamp Request Accepted (with millisecond accuracy)
https_frontendFront-End Name
wp_backend/wpTarget Request Was Routed To
0/0/5/287/750Time Waiting for Full Request from Client (ms) /
Time Waiting in Queues (ms) /
Time to Establish Connection to Destination Server (ms) /
Time for Destination Server to Send Response (ms) /
Total Time Request Active in HAProxy (ms)
200HTTP Response Code
34854Bytes Read
- -Optional Values:
  Captured Request Cookie
  Captured Response Cookie
----Termination State Preceded by: --
384/384/213/0/0Active Connections /
Front-End Connections /
Back-End Connections /
Server connections /
Retries
0/0Server Queue Size / Back-End Queue Size
"POST /wp-admin.php HTTP/1.1""HTTP Request"

It looks like some attacker is trying to access our WordPress admin login form.  Not cool!

Let's go ahead and set up an automated way to ban them.  First, we want to craft a regular expression (regex) that matches the line in the log, but won't hit a false positive.  If you're new to regular expressions, you can use the fail2ban-regex tool (included when you install Fail2Ban) to test your regex against the logs in question after you've set up the filter.

Now that we've crafted a regex to match the offending line, let's create a filter for Fail2Ban.

Create the following file (/etc/fail2ban/filter.d/haproxy.conf) and add this code to it:

[Definition]
failregex=^.*haproxy\[[0-9]+\]:<HOST>:.*"(GET |POST )/wp-admin.php HTTP/1.1"$
ignoreregex=

This filter definition file contains the failregex variable, where we define the regex Fail2Ban will use to remove and block offending IP addresses.

The two important parts are the HOST variable, where it grabs the IP address, and the path part, where it makes sure the offender is indeed trying to log in.  Once a request matches this regex and an IP address is logged, it then gets parsed by the jail config, so we need to enable it by adding the following to the end of the jail config file (/etc/fail2ban/jail.conf):

[haproxy-login]
enabled=true
bantime=4800
findtime=120
maxretry=5
filter=haproxy
logpath=/var/log/haproxy.log
port=http,https
ignoreip=

There are a few important variables to set here.

First, the logpath should correspond to where the log Fail2Ban needs to scan exists.  If you're using NGINX instead, the logpath should probably be something like /var/log/nginx/access.log.

The port in our case is standard (80, 443), but if you're using a non-standard port, you'll have to modify this accordingly.

The filter value should correspond to the filter we just created.

Finally, the bantime, findtime, and maxretry variables need to be set.  In plain English, our config is set so that if an attacker attempts to log in more than five times in 120 seconds (two minutes), their IP will be banned at the firewall level for 4800 seconds.  After the ban time has passed, the IP address will be unblocked from iptables and allowed access again.  That said, if you plan on using this in a system that you use frequently, you should probably add your IP address to the ignoreip variable to make sure you don't accidentally ban yourself from your own server.

In conclusion, while this trick isn't fool-proof, especially against major actors, for the average person it works pretty well and can save us a bit of money, as well as peace of mind that the service we're paying for won't randomly drop our account.

Return to $2600 Index