From my reddit post: reddit post
This week I secured my Home Assistants external access with CloudFlare ZTNA and having had to dig through many guides and posts, I thought this could help.
My needs were: external access by browser from any PC (So no VPN), Functioning app without a VPN on phone, and functioning Alexa for WAF. I will link articles and post screenshots where I got stuck.
So:
1 - Browser access from the exterior is secured by a successful Gmail login, then login to HA
2 - Phones need a client certificate so app can access HA + login
3 - Alexa lambda function can bypass authentication, but ONLY that lambda
Steps:
I will assume that you have a cloudflared tunnel already setup, if not : New Add-On: Cloudflared (you need managed tunnel I think)
1 - Create a tunnel and secure with Gmail or another Identity provider:
Follow the guide and Ignore the part about google IP’s?
2 - Create a 2nd tunnel and secure with mTLS for the android app (did not test on iPhone)
I had a problem with the WAF rules so here are mine (In SECURITY/WAF in the website menu):
1 - Create rule, name it, scroll down to “Edit expression” and enter:
(cf.tls_client_auth.cert_verified)
Then the action for that rule is “Skip”
2 - Create 2nd rule, name it, scroll down to “Edit expression” and enter while substituting https://yourapp-mtls.yourdomain.com for your domain:
(not cf.tls_client_auth.cert_verified and http.request.uri.path in {"https://yourapp-mtls.yourdomain.com"})
Then action for this is “Block”
3 - Modify Alexa Skill to work with ServiceAuth headers so it will work with ZTNA.
Inspired buy this post: https://www.reddit.com/r/homeassistant/comments/w6hnhz/help_with_alexa_and_cloudflare_tunnel/
and this comment by /u/btoconnor
1 - follow guide for Alexa access but substitute parts:
2 - Substitute lambda function by function here:
3 - Create a new lambda function with code her:
4 - Create Service Token for ZTNA and add policy to your tunnel
Service tokens · Cloudflare Zero Trust docs.
5 - Add CF_CLIENT and CF_SECRET env Variables to both lambda functions (only the actual keys as values)
6 - Substitute Access Token URI for your wrapper function url + /auth/authorize
7 - Link as usual
Hope this helps somebody