All of our servers, site and Discourse community (discussions.ftw.in) sit behind Cloudflare.
Can we set at the Discourse server level to only allow Cloudflare IPs and deny all other IP traffic?
We’ve been under a DDoS layer 7 attack and learned our main site IP was exposed.
To fix this, we updated our .htaccess on our main site server to only allow traffic from Cloudflare IPs and deny all other traffic. This does not let our origin server IP get exposed.
We are not exactly sure how to do the same above for our Discourse server at the server level. In my searches, I’ve seen cloudflare.template.yml mentioned and that it’s set to make sure we are seeing our user IPs, but I’m not sure.
I am reading this thread and we need to know this information as well. We are running Discourse using the docker install on an Ubuntu server hosted on AWS.
How would we whitelist Cloudflare IPs with this build? I just ssh’d in and did not see any obvious NGINX paths within the container.
Also not being familiar with nginx and yaml, I followed your instructions @riking and created a copy named ./templates/cloudflare.allowdeny.yml with the following code.
Can you please verify this will do the job? Much appreciated.
run:
- file:
path: /tmp/add-cloudflare-ips
chmod: +x
contents: |
#!/bin/bash -e
# Download list of CloudFlare ips
wget https://www.cloudflare.com/ips-v4/ -O - > /tmp/cloudflare-ips
wget https://www.cloudflare.com/ips-v6/ -O - >> /tmp/cloudflare-ips
# Make into nginx commands and escape for inclusion into sed append command
CONTENTS=$(</tmp/cloudflare-ips sed 's/^/allow /' | sed 's/$/;/' | tr '\n' '\\' | sed 's/\\/\\n/g')
echo CloudFlare IPs:
echo $(echo | sed "/^/a $CONTENTS")
echo "deny all;"
# Insert into discourse.conf
sed -i "/sendfile on;/a $CONTENTS\nreal_ip_header CF-Connecting-IP;" /etc/nginx/conf.d/discourse.conf
# Clean up
rm /tmp/cloudflare-ips
- exec: "/tmp/add-cloudflare-ips"
- exec: "rm /tmp/add-cloudflare-ips"
Well, you’re going to need the other rules too, so we might as well only download the list once. You missed replacing real_ip_header with deny all. Here’s a combined version:
run:
- file:
path: /tmp/add-cloudflare-ips
chmod: +x
contents: |
#!/bin/bash -e
# Download list of CloudFlare ips
wget https://www.cloudflare.com/ips-v4/ -O - > /tmp/cloudflare-ips
wget https://www.cloudflare.com/ips-v6/ -O - >> /tmp/cloudflare-ips
# Make into nginx commands and escape for inclusion into sed append command
CONTENTS1=$(</tmp/cloudflare-ips sed 's/^/allow /' | sed 's/$/;/' | tr '\n' '\\' | sed 's/\\/\\n/g')
CONTENTS2=$(</tmp/cloudflare-ips sed 's/^/set_real_ip_from /' | sed 's/$/;/' | tr '\n' '\\' | sed 's/\\/\\n/g')
echo CloudFlare IPs:
echo $(echo | sed "/^/a $CONTENTS1")
# Insert into discourse.conf
sed -i "/sendfile on;/a deny all;$CONTENTS1\n $CONTENTS2\nreal_ip_header CF-Connecting-IP;" /etc/nginx/conf.d/discourse.conf
# Clean up
rm /tmp/cloudflare-ips
- exec: "/tmp/add-cloudflare-ips"
- exec: "rm /tmp/add-cloudflare-ips"
Save that to your cloudflare.allowdeny and remove the stock version from your app.yml. And then test it, of course
This is great thanks @riking, but since you maintain cloudflare.template.yml I would not like to remove or mess with it, and let it download twice. …or does it download for every user or just when the container is built?
Nah, the file hasn’t changed in a while and I don’t think it will anytime soon. The primary priority here is to not break git pull, hence having a copy.
Yes, it’s only when the container is built; but the fastest code is the code that doesn’t run. Why download twice when you can do it once.
We couldn’t get this to work. Kept getting a 403. We tried moving “deny all;” around and we even tried breaking up the two files and still wouldn’t work.
We are on AWS and realized we can manage using the security group to allow and deny. This is working for us right now.
Appreciate the help though. I think a nice-to-have for those on Cloudflare is an option to turn on to whitelist only Cloudflare IPs through the method above. Perhaps an admin checkbox. It makes a lot of sense and great addition for security. And honestly a nice selling point!
Next thing we need to solve though… our IP is still showing up in emails. We are using SparkPost as our email vendor so we are scratching our heads why it’s still showing.
I don’t use AWS, but block access to our instance using firewall rules to filter out IPs, not on an approved list. Not sure what AWS offers in terms of firewall support.
I am using PFSense, and it can pull a list of IPs from a URL at an interval. I have scripts that create the approved IP list for PFsense to pull. IPs on the list make it to the forum, IPs not on the list are redirected to a page explaining the block.
I use this same technique to block access to Atlassian Confluence instance. Since it’s a firewall rule you can block any type of traffic you want, to any destination you want. Very flexible.