How we successfully deflected a layer 7 DDOS attack with Nginx and GeoIP

Written by - 0 comments

Published on - Listed in Nginx Internet Security


A few days ago it happened: Suddenly we got a storm of alerts on a couple of websites we host, and shortly after that we saw increasing load on the affected web server.

CPU Load during DDOS

As you can see in the CPU Load graph, the load spiked up sharply. But what would cause that? A huge traffic or visitor spike? Checking the network graph (using check_netio), didn't show that much of an increase:

Network graph

What is causing it?

Once we identified the website (a web shop) causing the load spikes, we first suspected a bug in the web application (Magento). This Magento shop runs behind a Nginx reverse proxy and uses Apache with mod_php. We straced Apache processes and saw loops in calling the same files of the Magento cache over and over again (but this turned out to be just a "correct" behavior because the same sites were opened over and over again - at this point in time we didn't know that yet).

Then we analyzed the Nginx access log files with goaccess and compared the data with the previous days and saw a jump in visits:

Visits during DDOS

By just looking at that graph (note: the screenshot of the graph was obviously taken once the system normalized), something very interesting shows up: The hits (blue) didn't grow in the same way the visits (red) did. If the shop would get a lot of legitimate visitor traffic, both lines would more or less grow in parallel. A quick look at some lines in the access log confirmed: This is a DDOS attack.

We're under attack!

Lesson one when you're under attack: Do not panic. Whatever you do, think clearly, make decisions based on facts and information. The DDOS attack won't probably go away in the next few hours anyway, so there's no need to rush to a (bad) decision.

Blocking the attacking IP's was literally impossible, as hundreds of thousands of IP addresses were used to attack. Probably a whole botnet just having fun... However using the goaccess summary of the web logs revealed something interesting: Almost all (97%) of the visits (red) originated from China:

GeoIP Visitor origin during DDOS

Again: Note the correlation between visits (red) and hits (blue). Europe and North America seemed to have real visitors, compared to the Asia graph.

Now that we knew the origin of the attack, we decided to completely block China using Geo Localization.

Nginx and GeoIP blockage

As mentioned before, the attacked web shop already used Nginx as a reverse proxy in front of Apache. Using the Nginx ngx_http_geoip_module, a geoip map can be created and actions based on that map can be created. I already used the geoip module in the past but for a different reason (see Different proxy_pass upstream depending on client ip address in Nginx).

The needed packages (geoip-database) were already installed on the server, so we could immediately start with the Nginx config:

    # Block countries
    geoip_country /usr/share/GeoIP/GeoIP.dat;
        map $geoip_country_code $allowed_country {
            default yes;
            CN no;
        }

A map is created based on the GeoIP country code and saved as variable $allowed_country. The default is set to "yes", if the looked up country code is "CN", the variable is set to "no". 

Inside the server {} context, the $allowed_country variable is read and if it matches "no", the request is blocked with an immediate http status 444 (connection closed without response):

  if ($allowed_country = no) {
    return 444;
  }

Note: Yes, I'm not a fan of "if" in Nginx either, but we got this solution running asap.

Success

As soon as we activated the GeoIP blockage, the server load and the web shop's response times normalized again. It took another 99'000 blocked ip addresses until the DDOS attack finally slowed down and stopped after 37 hours and 19 minutes.


Add a comment

Show form to leave a comment

Comments (newest first)

No comments yet.

RSS feed

Blog Tags:

  AWS   Android   Ansible   Apache   Apple   Atlassian   BSD   Backup   Bash   Bluecoat   CMS   Chef   Cloud   Coding   Consul   Containers   CouchDB   DB   DNS   Database   Databases   Docker   ELK   Elasticsearch   Filebeat   FreeBSD   Galera   Git   GlusterFS   Grafana   Graphics   HAProxy   HTML   Hacks   Hardware   Icinga   Influx   Internet   Java   KVM   Kibana   Kodi   Kubernetes   LVM   LXC   Linux   Logstash   Mac   Macintosh   Mail   MariaDB   Minio   MongoDB   Monitoring   Multimedia   MySQL   NFS   Nagios   Network   Nginx   OSSEC   OTRS   Office   OpenSearch   PGSQL   PHP   Perl   Personal   PostgreSQL   Postgres   PowerDNS   Proxmox   Proxy   Python   Rancher   Rant   Redis   Roundcube   SSL   Samba   Seafile   Security   Shell   SmartOS   Solaris   Surveillance   Systemd   TLS   Tomcat   Ubuntu   Unix   VMWare   VMware   Varnish   Virtualization   Windows   Wireless   Wordpress   Wyse   ZFS   Zoneminder