Cloudflare ZeroTrust + Cloudflared Add-On + Companion Apps === Better Security

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:

  1. Home Assistant is already up and running

  2. you already have an FQDN

  3. Cloudflared Add-On is installed and configured and connected to Cloudflare

  4. 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

  5. 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.

2 Likes

Thanks a lot for the guide, it works fine for me though I haven’t tested the iOS connection.
My question is if Cloudfare checks if the client certificate is mine or does it work with any certificate.

The Client Certificate checked for is the one you made.

Test it out by setting the rule to Skip or Block, and then refresh your Companion App, and view the CF Events in WAF.

1 Like

Thanks for your tips, really appreciate that!

However: Am i assuming right, that you have disabled your applications within Zero Trust? Because those skip rules do not skip the zero trust rules on my site.

Thanks for your quick feedback!

Yes, I have not added an Access (formerly Zero Trust) application for the sub.domain.tld I use for connecting my mobile devices.

I use the internal / external URL scheme. When internal, Companion app uses https://domain.tld and my DNS rewrites the IP to the LAN IP of the HA instance.

When external, Companion app uses https://sub.domain.tld which is on Cloudflare.

I have tried different configurations with setting a self-hosted application with Access and ingress rules. But the reality is that I don’t know I would ever use it; the only case would be if I needed to use a computer when not at home to view my HA. But this seldom happens.

I should provide a follow up:

My WAF rule 5 (free tier of Cloudflare allows 5 WAF rules) is a BLOCK rule.

The first 4 are all skip rules. With the and /or operators, the rules can get pretty complex.

Anyway, it’s my Home Assistant and no one but me needs access to it (and family members on Companion App).

As I have been monitoring the events logs, I could still see a lot of traffic getting through.

Probing for wordpress login endpoints, attempting to load favicons, and from all over the world.

Anyway, the best way I found to truly achieve LOCK DOWN mode is to set rule 5 as a Source IP with a BLOCK and set the IP to 0.0.0.0/0

Yes. That means block the entire internet.

Some more stuff that I wanted to work got blocked. But i just went through one by one examining the event logs and then devising a WAF rule to SKIP the blocked service.

I can’t install the certificate on my android phone. I have copied the two keys to files and get errors. I have converted it using commands like this but it won’t install. Can you elaborate on how you have done this?

openssl pkcs12 -export -in test.crt -inkey test.key -out test-combined.p12