While researching ways to improve the speed of this website, I recently found out that Cloudflare offers a free tier of service to personal websites and blogs. Everything I read made this seem like a pretty easy move and the benefits of increased security, speed, and DOS protection seemed like this was a good decision.
The decision was a good one and the benefits have materialized. That being said, I have had to learn a lot and full implementation has been a little complicated in trying to make my server as secure as possible. I did not know all of this was possible at the outset but here is what I have been able to do with Cloudflare.
Initially setting up Cloudflare is really easy. Simply sign up for an account, point your current domain records to the IPs provided by Cloudflare, and then allow Cloudflare to manage your DNS. This takes about 24 hours but does not require any down time on your website.
At this point hitting your domain directly will route all requests through the Cloudflare CDN with whatever options you have configured in their admin panel. In my case, the only major consideration I had to make was with respect to Crypto/SSL. Since I was running Let's Encrypt Certs on my page I had to configure Full (Strict) mode (I have since changed this, more on that later).
I also enabled most of the speed improving capabilities of Cloudflare. The only issue I noticed at this point was that PrismJS (a code syntax highlighting JS module) kept kicking back 404 errors in my web logs. If I put Cloudflare in Development (bypass) mode, I did not get the errors. After weeks of troubleshooting locally, I finally discovered that the
RocketLoader feature is in Beta and apparently has some issues with PrismJS. Disabling
RocketLoader solved that problem.
While perusing the Cloudflare settings (and wanting to enable all the security related ones), I found a setting for
Authenticated Origin Pulls. This setting passes a certificate in all requests directly to your server so that you can validate that the requests come from CLoudflare and nowhere else. This requires local configuration changes to your web server config.
The complication here is that I do some management of my website through an SSH tunnel so as not to expose my management interface. As such,
Authenticated Origin Pulls will fail when accessing my page from local host with the recommended config changes.
The update to make this work with Cloudflare is to set up different server blocks (in NGINX as least) for the primary website and for the admin portion much like this ServerFault article.
The block that you do NOT want to do client certificate checking on needs to come first in your config but requires no other changes (this took a few hours of troubleshooting). For all other server blocks, you will need to add these lines:
ssl_client_certificate /etc/ssl/certs/cloudflare.crt; ssl_verify_client on;
Then you will need to save the Cloudflare Public Key cert to the location listed above (or a location of your choosing). Restart your NGINX service and you should be good to enable this setting in your Cloudflare panel.
Also while perusing the security and crypto settings for Cloudflare I realized that they present certificates for SSL/TLS and that users never actually see my server certificate. In
Full (strict) mode, Cloudflare still validates my cert but there is no real benefit here. I also tend to have major issues renewing my Let's Encrypt certs (I think due to my web server config).
Because of this, I decided to ditch Let's Encrypt just use self-signed certs on my server end and rely on Cloudflare to present that valid certs to users. I just followed this Digital Ocean guide to configure my server with self-signed certs.
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt
And update these lines in the my NGINX config:
ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt; ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
Finally, set your Cloudflare SSL setting to
Full (ignore certificate errors).
At this point, everything was pretty secure and squared away through Cloudflare but my website was still accessible directly via IP. I already had a good set of rules for
iptables from here but they did not take into account this Cloudflare configuration. To update I needed to add these lines to my
# Accept HTTP and HTTPS from CLoudflare IPs only -4 -A INPUT -m set --match-set cf4 src -p tcp -m multiport --dports http,https -j ACCEPT -6 -A INPUT -m set --match-set cf6 src -p tcp -m multiport --dports http,https -j ACCEPT # Continue to allow localhost access -4 -A INPUT -i lo -p tcp -m multiport --dports http,https -j ACCEPT
Then to create and keep the IP sets up to date I followed the steps here I rolled those steps into a script that I will run as a
cron job on a weekly basis to ensure I get any updates.
echo "Destorying old IP Sets" ipset destroy cf4 ipset destroy cf6 echo "Creating new IP Sets" ipset create cf4 hash:net ipset create cf6 hash:net family inet6 echo "Getting latest lists from CLoudflare" for x in $(curl https://www.cloudflare.com/ips-v4); do ipset add cf4 $x; done for x in $(curl https://www.cloudflare.com/ips-v6); do ipset add cf6 $x; done echo "Reload IP tables" iptables-restore ip6tables-restore
All of this configuration should force all traffic to my server through Cloudflare except for traffic that I am tunneling over SSH to the server directly. I am taking advantage of as much of the security and speed improvements as I can from Cloudflare. I have also learned a TON more about server configs and using a CDN.
Cloudflare Client Certificate
-----BEGIN CERTIFICATE----- MIIGBjCCA/CgAwIBAgIIV5G6lVbCLmEwCwYJKoZIhvcNAQENMIGQMQswCQYDVQQG EwJVUzEZMBcGA1UEChMQQ2xvdWRGbGFyZSwgSW5jLjEUMBIGA1UECxMLT3JpZ2lu IFB1bGwxFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3Ju aWExIzAhBgNVBAMTGm9yaWdpbi1wdWxsLmNsb3VkZmxhcmUubmV0MB4XDTE1MDEx MzAyNDc1M1oXDTIwMDExMjAyNTI1M1owgZAxCzAJBgNVBAYTAlVTMRkwFwYDVQQK ExBDbG91ZEZsYXJlLCBJbmMuMRQwEgYDVQQLEwtPcmlnaW4gUHVsbDEWMBQGA1UE BxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEjMCEGA1UEAxMa b3JpZ2luLXB1bGwuY2xvdWRmbGFyZS5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4IC DwAwggIKAoICAQDdsts6I2H5dGyn4adACQRXlfo0KmwsN7B5rxD8C5qgy6spyONr WV0ecvdeGQfWa8Gy/yuTuOnsXfy7oyZ1dm93c3Mea7YkM7KNMc5Y6m520E9tHooc f1qxeDpGSsnWc7HWibFgD7qZQx+T+yfNqt63vPI0HYBOYao6hWd3JQhu5caAcIS2 ms5tzSSZVH83ZPe6Lkb5xRgLl3eXEFcfI2DjnlOtLFqpjHuEB3Tr6agfdWyaGEEi lRY1IB3k6TfLTaSiX2/SyJ96bp92wvTSjR7USjDV9ypf7AD6u6vwJZ3bwNisNw5L ptph0FBnc1R6nDoHmvQRoyytoe0rl/d801i9Nru/fXa+l5K2nf1koR3IX440Z2i9 +Z4iVA69NmCbT4MVjm7K3zlOtwfI7i1KYVv+ATo4ycgBuZfY9f/2lBhIv7BHuZal b9D+/EK8aMUfjDF4icEGm+RQfExv2nOpkR4BfQppF/dLmkYfjgtO1403X0ihkT6T PYQdmYS6Jf53/KpqC3aA+R7zg2birtvprinlR14MNvwOsDOzsK4p8WYsgZOR4Qr2 gAx+z2aVOs/87+TVOR0r14irQsxbg7uP2X4t+EXx13glHxwG+CnzUVycDLMVGvuG aUgF9hukZxlOZnrl6VOf1fg0Caf3uvV8smOkVw6DMsGhBZSJVwao0UQNqQIDAQAB o2YwZDAOBgNVHQ8BAf8EBAMCAAYwEgYDVR0TAQH/BAgwBgEB/wIBAjAdBgNVHQ4E FgQUQ1lLK2mLgOERM2pXzVc42p59xeswHwYDVR0jBBgwFoAUQ1lLK2mLgOERM2pX zVc42p59xeswCwYJKoZIhvcNAQENA4ICAQDKDQM1qPRVP/4Gltz0D6OU6xezFBKr LWtDoA1qW2F7pkiYawCP9MrDPDJsHy7dx+xw3bBZxOsK5PA/T7p1dqpEl6i8F692 g//EuYOifLYw3ySPe3LRNhvPl/1f6Sn862VhPvLa8aQAAwR9e/CZvlY3fj+6G5ik 3it7fikmKUsVnugNOkjmwI3hZqXfJNc7AtHDFw0mEOV0dSeAPTo95N9cxBbm9PKv qAEmTEXp2trQ/RjJ/AomJyfA1BQjsD0j++DI3a9/BbDwWmr1lJciKxiNKaa0BRLB dKMrYQD+PkPNCgEuojT+paLKRrMyFUzHSG1doYm46NE9/WARTh3sFUp1B7HZSBqA kHleoB/vQ/mDuW9C3/8Jk2uRUdZxR+LoNZItuOjU8oTy6zpN1+GgSj7bHjiy9rfA F+ehdrz+IOh80WIiqs763PGoaYUyzxLvVowLWNoxVVoc9G+PqFKqD988XlipHVB6 Bz+1CD4D/bWrs3cC9+kk/jFmrrAymZlkFX8tDb5aXASSLJjUjcptci9SKqtI2h0J wUGkD7+bQAr+7vr8/R+CBmNMe7csE8NeEX6lVMF7Dh0a1YKQa6hUN18bBuYgTMuT QzMmZpRpIBB321ZBlcnlxiTJvWxvbCPHKHj20VwwAz7LONF59s84ZsOqfoBv8gKM s0s5dsq5zpLeaw== -----END CERTIFICATE-----