Configuring the ufw firewall to allow Cloudflare IP addresses
I have a Linode running Ubuntu 16.04, and I use the ufw firewall.
I have a web site running on that server, originally accessible via HTTPS on port 443 from anywhere on the internet.
The domain for that web site is managed via Cloudflare. I want the site to be available only through the domain, and not via the Linode's IP address.
Cloudflare publishes the IP addresses it uses to access your web site: https://www.cloudflare.com/ips/
Here is a page describing the overall idea of using ufw to allow access to your web site only from those Cloudflare IP addresses: https://www.ajsalkeld.com/blog/tutorial/2016/08/01/how-to-use-ufw-to-whitelist-cloudflare-ips-ubuntu-debian-digitalocean/
In this repo https://github.com/Paul-Reed/cloudflare-ufw there is a script that does this: https://github.com/Paul-Reed/cloudflare-ufw/blob/master/cloudflare-ufw.sh
I modified it a bit so that:
- it uses the /tmp directory
- it uses a unique filename (containing the current process ID) when retrieving the Cloudflare IP addresses
- it specifically allows connections only on port 443 (you may want to allow connections on port 80 as well or instead)
- it just outputs to the screen the commands that it would issue using ufw; If the commands look sane/good to you, copy and paste them into your terminal to run them
Here is my script:
#!/bin/sh
cd /tmp
wget https://www.cloudflare.com/ips-v4 -O ips-v4-$$.tmp
wget https://www.cloudflare.com/ips-v6 -O ips-v6-$$.tmp
for cfip in `cat ips-v4-$$.tmp`; do echo "ufw allow from $cfip to any port 443 proto tcp"; done
for cfip in `cat ips-v6-$$.tmp`; do echo "ufw allow from $cfip to any port 443 proto tcp"; done
Once I ran the script and copied and pasted its output into a terminal, ufw was configured as follows:
# ufw status numbered
Status: active
To Action From
-- ------ ----
[ 1] 22 ALLOW IN Anywhere
[ 2] 443/tcp ALLOW IN 103.21.244.0/22
[ 3] 443/tcp ALLOW IN 103.22.200.0/22
[ 4] 443/tcp ALLOW IN 103.31.4.0/22
[ 5] 443/tcp ALLOW IN 104.16.0.0/12
[ 6] 443/tcp ALLOW IN 108.162.192.0/18
[ 7] 443/tcp ALLOW IN 131.0.72.0/22
[ 8] 443/tcp ALLOW IN 141.101.64.0/18
[ 9] 443/tcp ALLOW IN 162.158.0.0/15
[10] 443/tcp ALLOW IN 172.64.0.0/13
[11] 443/tcp ALLOW IN 173.245.48.0/20
[12] 443/tcp ALLOW IN 188.114.96.0/20
[13] 443/tcp ALLOW IN 190.93.240.0/20
[14] 443/tcp ALLOW IN 197.234.240.0/22
[15] 443/tcp ALLOW IN 198.41.128.0/17
[16] 22 (v6) ALLOW IN Anywhere (v6)
[17] 443/tcp ALLOW IN 2400:cb00::/32
[18] 443/tcp ALLOW IN 2405:8100::/32
[19] 443/tcp ALLOW IN 2405:b500::/32
[20] 443/tcp ALLOW IN 2606:4700::/32
[21] 443/tcp ALLOW IN 2803:f800::/32
[22] 443/tcp ALLOW IN 2c0f:f248::/32
[23] 443/tcp ALLOW IN 2a06:98c0::/29
I tested by browsing to my web site's domain (e.g. https://mysite.com) and it worked. Then I tried to browse to my server's IP address (e.g. https://123.45.67.89) and it did not work, as expected and as intended.
Update: January 3, 2018: Thank you to Florian Schulze who suggested the use of Cloudflare's authenticated origin pulls, described at https://support.cloudflare.com/hc/en-us/articles/204494148-Setting-up-NGINX-to-use-TLS-Authenticated-Origin-Pulls. With this method, you don't have to worry that Cloudflare may have changed its IP addresses (the reason why you would need to update your ufw rules periodically).
There is also TLS client side authentication, a feature described at https://support.cloudflare.com/hc/en-us/articles/115000088491-Cloudflare-TLS-Client-Auth. It is, however, available only to Enterprise Cloudflare customers.