For those of you that now me, you know that I am very paranoid about security. I feel that while I know how to make a secure system, it very easy to get it wrong and very hard to get it right. One of the things that always concerns me is having a lot of people randomly “poking” at my servers and possibly finding errors in my system.
I have turned to Cloudflare to help reduce my exposed attack surface. They have some great services, many of which are available for free. This particular post will simply be talking about using their Web Application Firewall (WAF).
Cloudflare acts as a Content Distribution Network (CDN) that actually helps to speed up your web-site. The short version of this is that a visitor connects to an IP address on the Cloudflare network which acts as a caching proxy to connect back to your actual server. This is how Cloudflare can provide both security and caching.
What happens if someone has your servers actual IP address instead of the one hosted by Cloudflare? This allows them to circumvent all of the Cloudflare provided security and attack your server directly.
If you are running a Linux server, it is actually easy to restrict incoming connections to your server to only be from trusted Cloudflare addresses. By closing this bypass you are guaranteeing that you always have the protection of Cloudflare in front of your server. This also means that you can use additional Cloudflare capabilities to minimize the number of unauthorized requests that come to your server, which also reduces server load.
NOTE: This script should work with any CDN that provides similar capabilities as Cloudflare does – but I have not tested anybody else.
Here is the script that I use to close this Cloudflare bypass. Please look for colored keywords to identify areas that you need to customize
#!/bin/sh # Script taken and modified from https://github.com/Paul-Reed/cloudflare-ufw/blob/master/cloudflare-ufw.sh # Safety - make sure you are authorized before we do anything if [ "$(whoami)" != "root" ]; then echo ABORT: You must be root to run this script exit 1 fi # Clear out all firewall rues echo y | ufw reset # Flush and remove all UFW rules in both the filter and nat tables /sbin/iptables -F /sbin/iptables -X /sbin/iptables -F -t nat /sbin/iptables -X -t nat # Safety to make sure that everything is really removed echo y | /usr/sbin/ufw reset # Remove backup copies that the reset command generates rm /etc/ufw/*201?????_* # Disable the firewall until rules are set and assign default policies /usr/sbin/ufw disable /usr/sbin/ufw default deny incoming /usr/sbin/ufw default allow outgoing # Check to see if OpenVPN rules have been added to UFW already # If the rules are not already there, add rules above to the before.rules file # NOTE: If you are not using OpenVPN, this block is not needed if [ $(cat /etc/ufw/before.rules | grep "OpenVPN routing" | wc -l) -eq 0 ]; then cat < /etc/ufw/before.rules.nat *nat :POSTROUTING ACCEPT [0:0] -A POSTROUTING -s ww.xx.yy.zz/8 -o -m comment --comment "OpenVPN routing" -j MASQUERADE COMMIT EOF fi # Merge the new before.rules into the existing before.rules cat /etc/ufw/before.rules > /etc/ufw/before.rules.orig cat /etc/ufw/before.rules.nat /etc/ufw/before.rules.orig > /etc/ufw/before.rules # NOTE: If you are not using OpenVPN, this block is not needed # Disable the firewall again to make sure all rules are really purged /usr/sbin/ufw disable # Enable the new firewall echo y | /usr/sbin/ufw enable # Put in some safety rules so you do not get locked out accidentally /usr/sbin/ufw allow from www.xxx.yyy.zzz/32 to any port 22,80,443 proto tcp comment "Safety - home computers" /usr/sbin/ufw allow from 127.0.0.1/32 comment "Safety - localhost" /usr/sbin/ufw allow from www.xxx.yyy.zzz/32 to any port 22,80,443 proto tcp comment "Safety - home router" /usr/sbin/ufw allow from www.xxx.yyy.zzz/32 to any port 22,80,443 proto tcp comment "Allow VPN users" /usr/sbin/ufw allow from any to any port 1194 proto udp comment "OpenVPN via UDP" /usr/sbin/ufw deny from www.xxx.yyy.zzz to 126.96.36.199 comment "Block multi-cast" echo Deny applied # Determine working directory DIR="$(dirname $(readlink -f $0))" cd $DIR # Get the authoritative lists of Cloudflare IP addresses wget https://www.cloudflare.com/ips-v4 -O ips-v4.tmp wget https://www.cloudflare.com/ips-v6 -O ips-v6.tmp mv ips-v4.tmp ips-v4 mv ips-v6.tmp ips-v6 # Loop through all of the Cloudflare IP addresses and authorize them for cfip in `cat ips-v4`; do /usr/sbin/ufw allow from $cfip to any port 443 proto tcp comment "Allow Cloudflare via TCP"; done for cfip in `cat ips-v6`; do /usr/sbin/ufw allow from $cfip to any port 443 proto tcp comment "Allow Cloudflare via TCP"; done #NOTE: You can repeat the above lines to add other rules or change the allowed ports # Enable the firewall rules echo y | ufw enable # Display the nat table to ensure rules are properly added /sbin/iptables -t nat -L -n
After that, I simply added this script to my system’s crontab for the root user – since this script requires permission to run. I do not know how often Cloudflare IP addresses update so I set this to run once per day and on a system reboot.
@reboot /path/to/script.sh | mail -s "resetUFW results - reboot" [email protected]_domain.com 0 4 * * * /path/to/script.sh | mail -s "resetUFW results" [email protected]_domain.com
Everything has been working great ever since.