Securing Apps with Cloudflare Access
Often times there are applications you need to run on premise for compliance reasons related to governance and security controls (yubikey, 2fa, auditing). Although a vendors product may have Google auth / security controls, it's good to be overly cautious and further protect access by restricting external access using a VPN device or something like tailscale in order to access internal endpoints. Cloudflare offers an additional option through their zero trust model in Cloudflare Access.
Cloudflare access supports a wide range of access control rules from multiple auth providers to fine grained access control of things like country, emails, IP ranges, browser type, certificates and request parameters such as path prefixes.
In this post we'll set up an installation of Metabase (the best business intelligence tool out there - seriously check them out) in Aptible and then secure it with Cloudflare access. Our final setup will be:
This way we'll ensure that end users must auth through Cloudflare access, still allow api endpoints to be accessed by known hosts if the path starts with /api, and restrict all other access. Indeed if you needed it to be on the private network you could also use cloudflared, I may post about this at a later time.
There is a possibility of MITM if someone else's service is hosted on CF (since they'd be able to hit your origin from CF ip space). However cloudflare doesn't allow someone to change the Host Header when accessing an IP address directly (this effectively means your origin nginx should be checking host header and routing based on it): community.cloudflare.com/t/not-possible-to-..
As a caveat, even with cloudflare access you should always secure the internal tool with it's own auth system as well. Also, do NOT put your databases on any external IP space (there's almost always better ways than hole punching - ex: vpc peering or a reverse ssh proxy). This article only applies for HTTP(s) Internal tools that operators will be using from chrome. Also all the domains here are just examples (metabase.avaitla16.com
may be totally down when you're reading this).
Step 1: Create App in Aptible
Step 2: Deploy to app
aptible deploy --app metabase --docker-image metabase/metabase
Step 3: Create endpoint and force ssl on it. Then confirm its running:
aptible config:set --app metabase FORCE_SSL=true
Confirm we see the following:
Now let's setup the ip allow list from here cloudflare.com/ips:
You should confirm you can no longer access the domain from your system by visiting the URL in your browser and seeing it hangs indefinitely.
Now it's time to setup cloudflare dns:
Once your nameservers have been pointed over create an CNAME record to aptible:
Now confirm you can access metabase.avaitla16.com
in your browser. Now time to setup cloudflare access by visiting dash.teams.cloudflare.com
. Setup your GSuite Organization auth here (follow the instructions provided):
Now you'll need to create 2 access control rules (the first is for metabase.avaitla16.com
and the other is for metabase.avaitla16.com/api
):
For the main application all you need to set is GSuite Auth:
For the API rule you can do either a bypass or a service token , but in both cases you should explicitly set the allowed IPS:
IP Restriction for the API path:
Some feedback for the Cloudflare team: it is difficult that all paths need to be considered their own separate application. I wish Cloudflare for teams would allow organizing paths in one central place, so you can easily view all rules at once regardless of path (some vendors have multiple path prefixes which causes a sprawl of cloudflare applications in the dashboard) - and they're not sorted alphabetically either!
Nevertheless, you should finally have the following setup. Run a few curls to make sure all the places that should have access correctly do indeed have access and ensure you cannot access /api from an ip that isn't in the allowed service auth list. We also have nice audit logs available:
Stay safe!
Footnotes:
You might notice that in a private browsing window if you've enabled Metabase's own GSuite auth you get the following error around authentication failing. You simply need to enable cookies by selecting allow all cookies:
You may also notice that randomly you see a message that says: Sorry, you don’t have permission to see that. This is noted here: github.com/metabase/metabase/issues/12097 and is due to CF WAF rules for sql injection. To by pass it you have a few options:
Bypass the WAF with a Firewall Rule
You can create a Firewall Rule with the bypass action for the WAF to be deactivated for a specific combination of parameters. You could for example only bypass the WAF for a specific URL and a specific IP or user-agent: developers.cloudflare.com/firewall/cf-firew..
Disable the affected WAF rule(s): This will reduce the security of the site, but will stop the requests from getting blocked/challenged. How do I configure the WAF?