Firstly, apologies for the hiatus. The last few years has been insanely busy for me on a personal and professional level. Rest assured, the tinkering has continued – its just that the blogging has been deprioritised instead.
Changes to my infrastructure include Unifi wifi throughout, a 24U rack, and multiple other additions including UPS, etc. I’ve also moved my website to be protected via the Cloudflare CDN network, given i’ve been using it for DNS management for years now and it provides a good number of free-to-use features such as DDoS protection, layer 5-and-below firewall functionality blocking naughty countries (etc), and other cool features I wont go into for now.
If you truly want to set your website to be served solely via Cloudflare (proxied) rather than clients using Cloudflare for a DNS lookup to get your IP and then access your website via your public IP (thus increasing your attack surface), you’ll want to ensure only Cloudflare CDN IP addresses can access your website via HTTP/S.
To do this, feel free to plagiarise my setup below.
First, you’ll need to ensure your web server(s) have ipset and wget installed:
apt-get -y install ipset wget
Once dependencies are installed, create a file and store it wherever you like (in my demo lab i simply placed it in /root for testing purposes).
root@cflb:/# nano /root/ips-v4
Within this file, you’ll need to copy the IPv4 addresses of Cloudflare which can be found here. As of today (06/03/2020, the list looked as below):
root@cflb:/# cat /root/ips-v4 173.245.48.0/20 103.21.244.0/22 103.22.200.0/22 103.31.4.0/22 141.101.64.0/18 108.162.192.0/18 190.93.240.0/20 188.114.96.0/20 197.234.240.0/22 198.41.128.0/17 162.158.0.0/15 104.16.0.0/12 172.64.0.0/13 131.0.72.0/22
Once you’ve created the list, you’ll need to create a script that takes that list and creates an ipset using it:
root@cflb:/# touch /etc/zones/ips-v4 root@cflb:/# nano /etc/allow-cf.sh
In this file, add the following:
# Create the ipset list ipset -N cloudflare-ips hash:net # remove any old list that might exist from previous runs of this script rm /etc/zones/ips-v4 # Pull the latest IP set for Cloudflare cd /etc/zones/ wget -P . https://www.cloudflare.com/ips-v4 # Add each IP address from the downloaded list into the ipset 'cloudflare-ips' for i in $(cat /etc/zones/ips-v4 ); do ipset -A cloudflare-ips $i; done
This creates an ipset called ‘cloudflare-ips’, pulls the IPv4 addresses down into a file at /etc/zones/ips-v4, and iterates through the file adding each subnet into the ipset. We will then ACCEPT/REJECT using this ipset.
Next, lets create our iptables rules that leverage this new set. In this example, I’ve created a file containing our iptables rules called /root/iptables-rules:
root@cflb:/# nano /root/iptables-rules
Within this file, you’ll need to add rules similar to the below:
# Get Cloudflare ipset built /etc/allow-cf.sh # First rules iptables -F iptables -N LOGGING iptables -I INPUT 1 -s 127.0.0.1 -j ACCEPT -m comment --comment "allow anything that comes from lo" iptables -I INPUT 2 -m state --state ESTABLISHED,RELATED -j ACCEPT -m comment --comment "allow anything established/related" iptables -I INPUT 3 -p tcp -m set --match-set cloudflare-ips src -m multiport --dports 80,443 -j ACCEPT -m comment --comment "Allow all Cloudflare IP's" iptables -I INPUT 4 -s YOUR_IP_HERE -d SERVER_IP_HERE -p tcp --dport 22 -j ACCEPT -m comment --comment "Home to servers via SSH" iptables -I INPUT 5 -j LOGGING -m comment --comment "logging for everything else" # Drop everything if it hasnt met an ACCEPT iptables -A LOGGING -m limit --limit 2/min -j LOG --log-prefix "IPTables-Dropped: " --log-level 4 iptables -A LOGGING -j DROP -m comment --comment "drop everything else after logging it"
Next, lets get chmod’ing:
root@cflb:/# chmod +x /root/iptables-rules root@cflb:/# chmod +x /etc/allow-cf.sh
Finally, run the script and voila, we’re protected:
root@cflb:/# bash /root/iptables-rules
If we look at the rules, you can now see the following:
root@cflb:/# iptables -nL --lin Chain INPUT (policy ACCEPT) num target prot opt source destination 1 ACCEPT all -- 127.0.0.1 0.0.0.0/0 /* allow anything that comes from lo */ 2 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED /* allow anything established/related */ 3 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 match-set cloudflare-ips src multiport dports 80,443 /* Allow all Cloudflare IP's */ 4 ACCEPT tcp -- MY_IP SERVER_IP tcp dpt:22 /* Home to servers via SSH */ 5 LOGGING all -- 0.0.0.0/0 0.0.0.0/0 /* logging for everything else */ Chain FORWARD (policy ACCEPT) num target prot opt source destination Chain OUTPUT (policy ACCEPT) num target prot opt source destination Chain LOGGING (1 references) num target prot opt source destination 1 LOG all -- 0.0.0.0/0 0.0.0.0/0 limit: avg 2/min burst 5 LOG flags 0 level 4 prefix "IPTables-Dropped: " 2 DROP all -- 0.0.0.0/0 0.0.0.0/0 /* drop everything else after logging it */
Any issues, hit me up via the contact form.