START TLDR
For many years I had cable internet, and even moving house and city a few times, the cable ISPs all provided unofficial static public IP address for the service.
It was straight forward to assign my FQDN to my IP through DNS. Maybe once every two years it would need an update, which I would do manually.
But that’s not really the best choice, and I switched to Fiber Internet (better prices, much better speeds). And my Fiber ISP seems to change my IP almost daily. The above previous “plan” to keep HA available fell apart quickly.
A second use case arose when I found that in addition to the HA front end, I also wanted to have separate points of entry for NodeRed and VaultWarden.
The best solution for this is the Cloudflared Add-On.
I won’t go into how to set this up in this post; that’s already been written many times, and the official guide is easy to follow.
The major benefit of Cloudflared, aside from hiding the actual IP address of my home internet, is being able to route multiple subdomains to various ports over a single tunnel. This is not possible with simple/traditional DNS.
Great! I’ve got Cloudflared setup with my Cloudflare ZeroTrust and then… it’s all broken! (Unless using a standard browser!)
So I disabled the Cloudflare ZeroTrust Application, and simply let all traffic pass through, with the exception of setting up a country block in the WAF. Along with a single webhook endpoint for Samsung Smartthings to send data to HA.
And I didn’t look much at any of that for many months. But I finally started looking into the monthly update reports Cloudflare had been sending. And it’s just absolutely SHOCKING just how much traffic is trying to scrape and troll nearly any endpoint that may be exposed.
There have been many posts in this forum, and elsewhere, that speak to this exact problem:
1. a desire to have an extra layer of some type of security in addition to the built-in
2. the need to have remote access to the services (in my case: HA and VaultWarden)
3. not to have the Companion Apps break completely, or function poorly, or randomly get locked out by Cloudflare trying to protect me from myself
After many hours and days of reading through far too many threads with really no one coming up with a decent solution, I have merged much of this disjointed information together and have come to a very compatible compromise.
END TLDR
Let’s assume the following pre-requisites:
-
Home Assistant is already up and running
-
you already have an FQDN
-
Cloudflared Add-On is installed and configured and connected to Cloudflare
-
Cloudflare ZeroTrust Access has a Self-Hosted Web Application configured with at least one IdP, and your HA instance is available.
-
easiest IdP is to use the CF TOTP, and restrict this to just your own email address, or to [email protected]
-
when loading your naked domain, the Cloudflare authentication page does not show the required email or email domain; someone attempting to gain entry would need to know your whitelist, and have access to that email
-
-
Congratulations! You’ve added additional security and authentication to your HA instance.
-
But ONLY when using a browser (Safari, Chrome, Firefox)
-
The HA Companion App will be totally broken, or partially functional. Or may work for a period of time until it unexpectedly fails partially or completely.
-
Same goes for VaultWarden / Bitwarden App
-
**
Let’s first focus on mTLS.
If you’re using Android for your Companion App, this is fairly straight forward.
Head into your Cloudflare Dashboard and get to SSL/TLS.
Create your Certificate.
Detailed instructions for installing the CF Root CA and your Client Certificate can be found here.
Next → Create mTLS Rule
This should be the first WAF Custom Rule. Straight forward: IF the hostname == sub.yourdomain.tld AND the Client Certificate is verified THEN Skip all remaining rules.
This allows for a bypass of the CF Access IdP login.
Remember to take your Client Certifcate pem and key and convert them to a p12, then copy this to your Android and add it to your trusted certificates.
When launching HA Companion App, you will be prompted for the Client Certificate. Select the one you installed, and that’s it! Your WAF rule will currently only pass traffic for your HA instance which is providing the Client Certificate (which no one but you has).
But this is only good for Android… What about iOS??
iOS Companion App doesn’t allow for Client Certificates.
And that’s where most get stuck, and turn off CF Access.
But there is a better way!
WAF Rules can Skip, Challenge, or Block when a rule matches True/False.
Let’s focus on contructing a complex rule which will SKIP when TRUE.
My template developed to become this:
What three key/value pairs in the WAF conditions, when combined, are likely only going to select me?
Through many trials, with both Skips and Blocks, and combing through the WAF Events, I decided on the following:
This is the first of FOUR conditions.
AS Num is a unique identifier Cloudflare uses to determine the ISP from which the incoming request originated.
Hostname is your sub.domain.tld
User Agent CONTAINS “Home Assistant”. (Although Android HA CompApp appears to only present a consistent user agent, iOS does not do this consistently. At first I had this set to equals, but it started to fail. Hence contains is strong enough.
And this rule is SKIP. So, when the following condition is TRUE: the ISP == myISP AND the hostname == mydomain.tld AND the UA == Home Assistant, the request SKIPS all remaining rules.
Primarily I use two ISPs, my home internet, and my mobile carrier. Go ahead and add an OR rule with another set of AND statements to capture your mobile ISP. If for some reason I would be in a condition that I cannot SKIP the WAF on my iPhone, I also have my Android with the private Client Certificate.
If required, also add a full set of ASNum + Hostname + UA (contains) Bitwarden Mobile.
Finally, add another WAF Rule to SKIP on Webhooks.
Some posts had suggested wildcarding the webhooks to SKIP all webhooks. But that’s just a bit too open door for me!
It’s easy to just provide the URI Full webhook endpoint.
My webhooks are for Smartthings and something else I’ve now forgotten… It’s also an option to add the webhook endpoints of your Companion Apps. I believe that once set, these remain the same (?).
Something fancier would be an automation to monitor the device webhook and update the WAF vai CF API when necessary.
Finally, add another rule to BLOCK.
The simplest key would be Country. And here’s the catch:
Country EQUALS YourCountry
OR
Country DOES NOT EQUAL YourCountry
BLOCK.
This rule would effectively BLOCK ALL TRAFFIC that hasn’t previously passed on the above rules.
I think that this is a very good workaround / compromise for the Companion Apps not dealing with third party Identity Providers.