Port forward using iptables to web server only if source is CloudFlare

Looking at my web server’s access logs, not surprisingly I discovered traffic originating from IP addresses not belonging to CloudFlare. Initially, I was using Apache .htaccess to allow incoming traffic only if they originated from CloudFlare. This was somewhat sufficient but some hackers continued attempting to find loopholes instead of giving up so I needed to up the level of security and decided to look at ways to drop unwelcome traffic using OpenWRT firewall.

Scheduled download of CloudFlare IP list

Firstly, create a new folder under /etc/cloudflare for storing IPv4 addresses of CloudFlare. Run the following command to download and store the initial lists into this folder:

wget -q https://www.cloudflare.com/ips-v4 -O /etc/cloudflare/ips-v4

Verify that ips-v4 file exist before proceeding. You can verify by typing:

cat /etc/cloudflare/ips-v4

You should see a list of IP addresses.

Under OpenWRT Scheduled Tasks, I then added the following lines to:

  1. Download ips-v4 and store it with .tmp file extension. This prevents a failed retrieval from overwriting the last good file.
  2. Restart firewall only if the retrieval of list succeeded. Using CRON syntax, this task is performed every 1st day of the week at 3am.
# Download updated list of CloudFlare IP addresses and restart the firewall
0 3 * * 1 wget -q https://www.cloudflare.com/ips-v4 -O /etc/cloudflare/ips-v4.tmp && mv /etc/cloudflare/ips-v4.tmp /etc/cloudflare/ips-v4 && /etc/init.d/firewall restart

Note: You need wget and ca-certificates packages to be installed.

Port forward only if source is CloudFlare

Under OpenWRT Firewall Custom Rules, I added the following lines:

# Forward only incoming HTTPS traffic from CloudFlare to web server
for cfip in `cat /etc/cloudflare/ips-v4`; do iptables -A PREROUTING -t nat -i <WAN interface> -s $cfip -p tcp --dport https -j DNAT --to <web server IP>:443; done
iptables -A FORWARD -p tcp -d <web server IP> --dport https -j ACCEPT

What this does is when the firewall is initialising, it loads the list of IPv4 addresses (already downloaded by the scheduler) and creates one PREROUTING rule per line of IPv4 address to allow port forwarding the HTTPS port 443 while all other traffic sources will be dropped by default. Be sure to replace <WAN interface> and <web server IP> with your settings.

Note: I have no use for IPv6 at the moment so I have not added firewall rules for them.

For security reasons I am forwarding HTTPS port 443 only. I strongly recommend that you do the same. If you want HTTP port 80 to be forwarded as well, add the following:

# Forward incoming HTTP traffic from CloudFlare to web server
for cfip in `cat /etc/cloudflare/ips-v4`; do iptables -A PREROUTING -t nat -i <WAN interface> -s $cfip -p tcp --dport http -j DNAT --to <web server IP>:80; done
iptables -A FORWARD -p tcp -d <web server IP> --dport http -j ACCEPT