HOWTO: Secure Cloudflare Tunnels remote access

I have remote access to my HomeAssistant with Clouflare tunnel. Is there a way to use authenticator app on iphone to open the tunnel (like LastPassAuthenticator)?
Reason I ask is that companion app on iPhone does not work (or at least I have not managed to get it to work) with 2FA using email or Google…

I think I missed something but I can’t find what.

I configured the CF Tunnel via docker-compose:

homeassistant:
  container_name: homeassistant
  image: "ghcr.io/home-assistant/home-assistant:stable"
  volumes:
    - ./homeassistant/config:/config
    - /etc/localtime:/etc/localtime:ro
    - /run/dbus:/run/dbus:ro
  restart: unless-stopped
  env_file:
    - shared.env
  devices:
    - /dev/ttyACM0:/dev/ttyACM0
  privileged: true
  network_mode: host

tunnel:
  container_name: cloudflared-tunnel
  image: cloudflare/cloudflared
  restart: unless-stopped
  env_file:
    - shared.env
  command: tunnel run
  environment:
    - TUNNEL_TOKEN=${TUNNEL_TOKEN}
  network_mode: host

It is running and I see it connected well:

Logs:

cloudflared-tunnel | 2024-04-09T17:06:18Z INF Updated to new configuration config="{\"ingress\":[{\"hostname\":\"<masked-hostname>\",\"originRequest\":{},\"service\":\"http://localhost:8123\"},{\"service\":\"http_status:404\"}],\"warp-routing\":{\"enabled\":false}}" version=11

When I’m connecting to that hostname, I need to verify my email.

But after verifying I get Bad Gateway 502 from Cloudflare.

The log in the tunnel client is:

cloudflared-tunnel | 2024-04-09T17:24:44Z ERR  error="Unable to reach the origin service. The service may be down or it may not be responding to traffic from cloudflared: dial tcp 127.0.0.1:8123: connect: connection refused" connIndex=1 event=1 ingressRule=0 originService=http://localhost:8123
cloudflared-tunnel | 2024-04-09T17:24:44Z ERR Request failed error="Unable to reach the origin service. The service may be down or it may not be responding to traffic from cloudflared: dial tcp 127.0.0.1:8123: connect: connection refused" connIndex=1 dest=https://<masked-hostname>/ event=0 ip=198.41.200.23 type=http
cloudflared-tunnel | 2024-04-09T17:24:44Z ERR  error="Unable to reach the origin service. The service may be down or it may not be responding to traffic from cloudflared: dial tcp 127.0.0.1:8123: connect: connection refused" connIndex=1 event=1 ingressRule=0 originService=http://localhost:8123
cloudflared-tunnel | 2024-04-09T17:24:44Z ERR Request failed error="Unable to reach the origin service. The service may be down or it may not be responding to traffic from cloudflared: dial tcp 127.0.0.1:8123: connect: connection refused" connIndex=1 dest=https://<masked-hostname>/favicon.ico event=0 ip=198.41.200.23 type=http

My HomeAssistant is accessible at the same time from the internal IP, and from inside the docker-compose machine I can curl localhost:8123 and get the right HTML.

The configuration.yml includes the next part:

http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 172.30.33.0/24

And of course, I restarted the docker-compose many times.

(Do you need any more info?)

Update:
Ok, first I found that even with the installed tunnel connector, I need to configure in the Tunnel settings the local IP address (192.168.1.23) instead of the localhost.
Second, I need to check which addresses are accessing to the HA and allow them in the proxy config.

1 Like

I get a “Cannot reach [test] myapp” error if I put the access rules in place when linking. If I remove the access rules, I am able to link Google Assistant. are the access rules supposed to be activated after the first link has been established?

I couldn’t connect with cast dashboard. You may need to include websocket in the rule: /api/websocket
This works for me:

(http.host eq “my.homeassistant.com” and not cf.tls_client_auth.cert_verified and http.request.uri.path eq “/api/websocket/”)

It’s probably an header issue.
God luck!

Hello

One short question how , If it is possible, set an aditiona host

additional_hosts:
  - hostname: router.example.com
    service: http://192.168.1.1

But my sevice i Want to point to a diferent homeassitant instantnce in a diferent location, so it’s not o the same network.
It is posibel to resolve it.

Hi All

Just thought I would post that I have had success with accessing the HA app without getting the Cloudfare ‘forbidden’ message while outside of the network, and also without breaking google home.

Unsure if this is the right way of going about it but adding another tunnel hostname in Cloudfare pointed at the same domain but adding a subdomain worked a treat.

To explain further:

  • Following the instructions above I managed to get google assistant working and local access to the HA instance. However attempting to externally access at https://xxxxxxxx.net through the HA app I could not get past the Cloudfare ‘Forbidden’ or the email authentication screen.
  • In Cloudfare under the existing tunnel created as documented, under Public Hostname, I added another hostname with subdomain, https://house.xxxxxxxx.net
  • Pointing the HA app external URL to this new hostname allows access without trouble, and, testing Google Home functionality everything is visible and online.

If this looks incorrect and I have rendered my instance unsafe in any way please let me know and I will go back to choosing which function to stop using, otherwise I hope this helps someone else.

The intent is to allow secure access over CF tunnels, but you’re exposing your HA to the internet directly via that new hostname. When HA has a remote compromise (and everything has one sooner or later) you’ll be wide open. I suggest adding authentication instead via github, google, etc.

Note this will not make the mobile app work; there’s a hacky workaround with certs on android but not aware of anything on iOS. I don’t care about this myself as I’m always VPN’d to my home LAN anyway.

Hoping someone can assist, I can’t get header auth to work through cloudflare anymore.

A little bit about my setup is I use cloudflared and cloudflare access. I validate the user from cloudflare access using their header.

username_header: Cf-Access-Authenticated-User-Email

So if I set the header in the browser and browse locally it works and I see the header in the debug log.

2024-06-12 22:32:35.069 DEBUG (MainThread) [custom_components.auth_header] <CIMultiDictProxy('Host': 'home.net.xxxx.nz', 'Connection': 'keep-alive', 'Content-Length': '131', 'sec-ch-ua': '"Google Chrome";v="125", "Chromium";v="125", "Not.A/Brand";v="24"', 'sec-ch-ua-platform': '"macOS"', 'sec-ch-ua-mobile': '?0', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36', 'Content-Type': 'text/plain;charset=UTF-8', 'Accept': '*/*', 'cf-access-authenticated-user-email': '[email protected]',  
2024-06-12 22:32:35.069 DEBUG (MainThread) [custom_components.auth_header] Got actual IP 192.168.2.197

However when I connect through Cloudflare Access it errors with “unable to connect to homeassistant”

Log says: " Login attempt failed

Login attempt or request with invalid authentication from xyz"

However - the relevant header is there…

2024-06-12 22:34:12.207 DEBUG (MainThread) [custom_components.auth_header] <CIMultiDictProxy('Host': 'ha.xxxx.xy', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36', 'Content-Length': '127', 'Accept': '*/*', 'Accept-Encoding': 'gzip, br', 'Accept-Language': 'en-GB,en-US;q=0.9,en;q=0.8', 'Cdn-Loop': 'cloudflare', 'Cf-Access-Authenticated-User-Email': '[email protected]', 'Cf-Access-Jwt-Assertion':

Proxy config set to:

 use_x_forwarded_for: true
  trusted_proxies:
        - 192.168.2.0/24 
        - 103.21.244.0/22
        - 103.22.200.0/22
        - 103.31.4.0/22
        - 104.16.0.0/12
        - 108.162.192.0/18
        - 131.0.72.0/22
        - 141.101.64.0/18
        - 162.158.0.0/15
        - 172.64.0.0/13
        - 173.245.48.0/20
        - 188.114.96.0/20
        - 190.93.240.0/20
        - 197.234.240.0/22
        - 198.41.128.0/17

Been scratching my head. It used to work. - I could go back to sticking HA Proxy at the end of the cloudflare tunnel and then parsing the JWT which is also in the header and applying a header. I wondered if it might have some similarities to the nginx reverse proxy posts around the auth failure message. There’s not enough debug to see if auth_header successfully parsed the header which is present.

Fixed this - had an MTLS rule applying upstream in cloudflare over /api and didn’t have the certificate loaded on the device…

The way I have decided to go about remote access via the HA app using cloudflare tunnels has just been using the WARP app and allow authentication via WARP on my access app. It isn’t ideal but within the WARP app on my phone I have configured it to exclude every app except for home assistant in hopes that it wont demonstrate the same passive battery drain that having VPNs open all the time tend to do on android.