Secwall's notes


Configuring ferm to allow connection only from cloudflare CDN

Posted on

Problem

VPS for this site is really slow. I wanted to speed up site load a bit. So I signed up on Cloudflare and set up CDN for it.

What now? Site is still available via ip directly (So if someone want to find it's real ip he/she could just scan entire internet and try to make GET / request with HOST header secwall.me - not so easy task, but real).

This is really THE issue if you are going to use cloudflare as DDoS protection. Attacker task may be really simplier if he/she knows that you use specific hoster (Leaseweb/Digital ocean/etc.).

Solution

Warning! This makes your site unavailable without cloudflare. You will need to disable firewall protection to make it work.

I prefer to use ferm for automatic iptables configuration. Cloudflare gives us a list of it's ip addresses in machine parseble format.

Here is simple shell script for downloading this list and checking if it was changed:

#!/bin/sh

wget https://www.cloudflare.com/ips-v4 -O /tmp/cloudflare-ips-v4.list

LOCAL=`md5sum /etc/ferm/cloudflare-ipv4.list | cut -d\  -f1`
CLOUD=`md5sum /tmp/cloudflare-ips-v4.list | cut -d\  -f1`

if [ "$LOCAL" != "$CLOUD" ]
then
    mv /tmp/cloudflare-ips-v4.list /etc/ferm/cloudflare-ipv4.list
    /etc/init.d/ferm reload
fi

And example ferm configuraition:

domain ip {
    table filter {
        chain INPUT {
            policy DROP;

            mod state state INVALID DROP;
            mod state state (ESTABLISHED RELATED) ACCEPT;

            interface lo ACCEPT;

            @def $CLOUDFLARE = `cat /etc/ferm/cloudflare-ipv4.list`;
            interface eth0 proto tcp saddr $CLOUDFLARE dport 80 ACCEPT;

            proto icmp ACCEPT;

            proto tcp dport ssh ACCEPT;
        }
        chain OUTPUT {
            policy ACCEPT;

            mod state state INVALID DROP;
            mod state state (ESTABLISHED RELATED) ACCEPT;
        }
        chain FORWARD {
            policy ACCEPT;

            mod state state INVALID DROP;
            mod state state (ESTABLISHED RELATED) ACCEPT;
        }
    }
}

domain ip6 {
    table filter {
        chain INPUT {
            policy DROP;

            mod state state INVALID DROP;
            mod state state (ESTABLISHED RELATED) ACCEPT;

            proto (ipv6-icmp icmp) ACCEPT;

            proto tcp dport ssh ACCEPT;
        }
        chain OUTPUT {
            policy ACCEPT;

            mod state state INVALID DROP;
            mod state state (ESTABLISHED RELATED) ACCEPT;
        }
        chain FORWARD {
            policy ACCEPT;

            mod state state INVALID DROP;
            mod state state (ESTABLISHED RELATED) ACCEPT;
        }
    }
}

Shell script should be placed is cron.hourly (or daily). This will make rules update automatic.