Home Assistant Remote Access with Cloudflare Argo

In this post, I will walk through how to setup Argo Tunnels from Cloudflare to remotely access your Home Assistant instance from anywhere. This will cost USD $5 a month plus 10 cents per GB of bandwidth, but also allows you to proxy out more than just Home Assistant, all included in the same $5 plan. You also need a device inside your home network - in this tutorial I’ll be using a Linux VM - to act as the outbound connector to Argo.

VERY IMPORTANT NOTE: Do not proceed with this if you have trusted networks enabled in Home Assistant. If the VM you create further down is inside the trusted network range, you will allow ANYBODY on the internet to access your Home Assistant installation as if they were within your network. IF you must continue, then MAKE SURE the VM you use is outside of the trusted network range, or MAKE SURE you add in Cloudflare for Teams with authentication in front of your Argo tunnel.

A few shortcuts:

Remote Access Options

First, let’s discuss some of the remote access options you have with Home Assistant. There may be other options, but generally you can use any of the following:

  • Port forwarding on your router
  • Nabu Casa’s remote access
  • Client VPN
  • Reverse proxy solution with/without VPN
  • Complex reverse proxy setups with SSH tunnels
  • Cloud-based reverse proxy, such as Argo

You can find plenty of information online about each of these, and they all have some pros and cons. Personally, I use Nabu Casa’s remote access for seamless callbacks and one-click Google Assistant integration, and Argo for the Home Assistant app and web browser access when not at home.

Reverse Proxies

Reverse proxies generally act as an intermediary between the client and the server. Normally you might point your website to an Apache web server directly, and that Apache web server and the Linux server it sits on would have a public IP address directly assigned to it. However, for various reasons - whether load balancing, acceleration, security, or others - you might decide to have a reverse proxy handle the web request, and pass that request off to the Apache web server on behalf of the client. This way, you can have multiple Apache web servers to load balance the request, or you could isolate the Apache web server in a more protected area of the network instead of having it directly exposed.

For Home Assistant, using Nginx or other proxies allows you to more easily install an SSL certificate and protect Home Assistant itself from direct attacks from the internet. The concept remains the same: point your domain to your Nginx proxy, and configure Nginx to redirect requests onward to Home Assistant.

You might notice that everything I mention here is talking about inbound connections. This means, one way or another, you’ll still need inbound access to your home network, whether via port forwarding, site-to-site VPN from Azure, or similar. It might be that you provide inbound access to Home Assistant and place Nginx somewhere on the internet - or maybe you have Nginx inside your network as well, and you provide inbound access to Nginx.

Either way, you’ll be:

  • Opening some sort of inbound access to your network
  • Add port forwarding rules or site-to-site VPN
  • Keeping track of your dynamic IP address from your ISP or using a dynamic DNS service
  • Exposing inbound traffic to your home
  • Unable to do any of this if you don’t have a publicly routable IP address with your internet connection

Re-thinking Reverse Proxies

What if we could do away with the downsides of the reverse proxy method? What if we could avoid port-forwarding rules or VPN setups? What if you could tunnel into your network from a connection being opened outbound from your network?

Microsoft has offered this capability with web applications for a long time with their Azure AD Application Proxy. The downside with this is you need premium Azure capabilities and Windows servers. Not exactly very friendly for home network admins.

Enter Cloudflare and Argo.

Cloudflare, Argo Tunnels, and Cloudflare for Teams

Quick explanation of what these are. Skip these if you already know.

Cloudflare

Cloudflare is a content delivery network (CDN) which handles the initial requests to your content. You point your domain to cloudflare, and they handle the traffic, and deliver any static content to the user immediately. Anything that cannot be cached by them, they pull from the “origin”, which is your actual web server. For large websites with global visitors, this can make the website load more quickly. It also puts Cloudflare as the initial receiver of any request, which means some of the security risks of having content on the internet are offloaded to them. Cloudflare essentially acts as a reverse proxy, but delivered as a service and not via you configuring your own Nginx or similar reverse proxy tool.

Argo Tunnels

Argo adds some additional capabilities to Cloudflares CDN, including what they call “Smart Routing”. Turns out a part of this includes Argo Tunnels, which appear to have been updated in late 2020 to be more stable. Here’s an October 2020 announcement about Argo Tunnels that live forever. Cloudflare has a great graphic on the linked page which shows how the client and the server both make requests to the Cloudflare cloud and “meet in the middle”.

Cloudflare for Teams

Cloudflare offers an enterprise solution called Cloudflare for Teams which extends the Argo Tunnel capability with added support for OAuth authentication. This is interesting because it enables you to add Cloudflare-provided authentication, either via one-time passcode sent to email or even Google Account authentication, in front of your applications. However, I would recommend skipping this for Home Assistant remote access because the Home Assistant app doesn’t know how to handle this authentication. You could consider adding this authentication in front of other applications you might expose, such as your Proxmox server, your NVR, etc.

Getting Started

Ok, now that you have the context… let’s get going!

Setting Up Cloudflare

First, you need a domain name. If you don’t have one, then go register one through one of the many registrars out there. I’m in no way affiliated with them, but I use I Want My Name because they have a huge selection of country code and custom TLDs. I’ll be using a spare domain I own, stringcheesefactory.com, for this example. You will need to migrate the nameservers to use Cloudflare’s nameservers, so you may want to consider using a new domain, or one which you don’t use for many other services, such as AWS S3 static site hosting, etc - unless you know how to handle the disruption.

Next, you need a Cloudflare account. Enter your email and a password, and then on the next screen, select “Get started” under “Cloudflare - Protect your internet presence”.

Enter the domain you will be using, and on the next screen, select the free plan, which is hidden at the bottom:

Cloudflare will scan the DNS records it can find (note: if you have some unique ones, it will not be able to find those - this requires a zone transfer which DNS providers generally prevent), and suggest some configurations. For me, because I am wiping away all of the DNS configurations for this domain, I will just delete anything currently existing:

Click continue, and confirm, when it asks you to acknowledge that you will add records later.

Next, Cloudflare will guide you through the process of changing your nameservers. They will assign you some nameservers to use - in my case:

brian.ns.cloudflare.com
carlane.ns.cloudflare.com

Next, Cloudflare will try to apply some settings to your site to improve security and performance. You can use the defaults.

Finally, you’ll be at your overview page. For now, you may not be able to do much more until the nameserver changes are completed, which may take a few minutes to a few hours. Refresh periodically until you see “Great news! Cloudflare is now protecting your site”.

Let’s tweak a few settings for now. Navigate to the following blue buttons and make these changes:

  1. Select “Caching”, then “Configuration”. Disable “Always Online”.
  2. Select Network and:
    1. Disable Onion Routing
    2. Disable IP Geolocation
  3. Select “SSL/TLS” and change mode to “Flexible”

Enabling Argo

Now that your domain is active on Cloudflare, you need to enable Argo. On your Cloudflare dashboard, select your domain, then “Traffic”, and review the pricing they list. At time of writing, it is USD $5 per per month, plus $0.10 (10 cents) per gigabyte after 1GB. Assuming you’re ok with this, click “Enable Argo” and enter your billing details. Once done, you’ll be back on the dashboard and should see Argo enabled.

Setting up a VM

Here’s where things will get a bit different for each reader. Cloudflare’s Argo Tunnel uses a daemon, called cloudflared, to make the connection outbound to Cloudflare to enable traffic to reach your network. You can read the installation documentation on Cloudflare’s documentation site.

For this tutorial, we’ll be following the VM approach, and will be using an Ubuntu-based VM, which uses .deb packages. You can also follow directions on their documentation site for installation on .rpm systems, Mac, Windows, or Docker.

Assuming you have a machine to install cloudflared on, go ahead and get that machine (or VM) ready. For this tutorial, I have a fresh install of Ubuntu server installed on Proxmox, with 1 CPU core, 2GB of RAM, and 32GB of disk.

If you would like to follow along, here are the settings I used in Proxmox:

I mostly used defaults on Ubuntu installation, however I did enable SSH server so I would not have to always use the Proxmox console.

Once logged in to the VM, run the following to make sure everything is updated:

sudo apt-get update && sudo apt-get -y upgrade && sudo reboot

Setting up Argo Tunnel using cloudflared

Once you’re back in to the VM, we’ll do the following - however, please validate the .deb package location from Cloudflare’s documentation site to make sure you’re pulling the latest/correct version:

wget -q https://bin.equinox.io/c/VdrWdbjqyF/cloudflared-stable-linux-amd64.deb
sudo dpkg -i cloudflared-stable-linux-amd64.deb

Next, you need to authenticate cloudflared to your Cloudflare environment We’ll be following these directions from Cloudflare. Run the following command:

cloudflared tunnel login

A few things happen here:

  1. Copy the red highlighted URL and paste it in to the browser you used to setup your Cloudflare account
  2. Select the domain you just added
  3. Authorize cloudflared to modify your Cloudflare instance
  4. Go back to your SSH session and confirm it downloaded the certificate

This is what it will look like:

Next, we need to establish the tunnel between your VM and Cloudflare. You can name the tunnel anything you like - and the tunnel can serve multiple applications. So, we might decide to name it “homeNetworkTunnel”, for example.

cloudflared tunnel create homeNetworkTunnel

Cloudflared tells me it created the tunnel with a specific UUID; you’ll want to note this UUID down.

Created tunnel homeNetworkTunnel with id 9b28d7ca-823b-4706-a039-01df951e06a6

Cloudflared knows how to direct traffic based on various “ingress rules”, which determine specific inbound DNS requests, which we’ll handle in a moment, and how those are mapped to your applications. We’ll go ahead and make a configuration file which has the ingress rules, but first you’ll need to modify a few things below:

  1. Change the tunnel UUID in line 1 to what cloudflared gave you
  2. Change the tunnel UUID in line 2 to what cloudflared gave you
  3. Update line 5 to be the correct FQDN you’ll be using to access Home Assistant
  4. Update line 6 to be the correct internal IP address for Home Assistant
tunnel: 9b28d7ca-823b-4706-a039-01df951e06a6
credentials-file: /home/ubuntu/.cloudflared/9b28d7ca-823b-4706-a039-01df951e06a6.json

ingress:
  - hostname: homeassistant.stringcheesefactory.com
    service: http://10.10.3.1:8123
  - service: http_status:404

We’ll save this file to /etc/cloudflared/config.yml:

sudo mkdir /etc/cloudflared
sudo nano /etc/cloudflared/config.yml
Paste the file contents in, and then confirm it looks ok:

cloudflared tunnel ingress validate

Assuming all checks out, let’s run the tunnel temporarily:

cloudflared tunnel run

Great, now let’s get the DNS records setup! Back on Cloudflare dashboard, select your domain again, and now select DNS, followed by “Add record”.

  1. Select Type of “CNAME”
  2. In the “name” box, enter the subdomain you put for the hostname, so for me it is “homeassistant”
  3. In target, we’ll enter the UUID followed by “.cfargotunnel.com”. So for me, I enter: “9b28d7ca-823b-4706-a039-01df951e06a6.cfargotunnel.com”
  4. Click save

Next, let’s visit it and see if it works! I’ll head on over to https://homeassistant.stringcheesefactory.com:

Since it works fine, let’s go ahead and install the cloudflared tool as a service. So, back to your SSH window. First, CTRL+C to kill the tunnel.

Next, let’s run the following to install the service and reboot the machine to make sure it starts on boot:

sudo cloudflared service install
sudo reboot

Wait for the server to come back up, and then try out your remote access URL again to confirm it works.

Update external URL for Home Assistant

In the main Home Assistant configuration.yaml file, make sure you add in the external URL you selected. For me, this would be:

homeassistant:
  external_url: "https://homeassistant.stringcheesefactory.com"

While you’re at it, make sure again that you don’t see any “trusted networks” in this same code block. You’ll have other pieces in your yaml file here, so don’t replace - I’m only specifying the components to review.

homeassistant:
  external_url: "https://homeassistant.stringcheesefactory.com"
  auth_providers:
    - type: homeassistant

Security

Some security tips worth repeating:

  1. Make sure trusted networks is disabled, or you account for it being enabled with your network design
  2. Utilize Cloudflare for Teams to enable authentication to anything you expose externally
  3. Keep in mind that any remote access - whether port forwarding or Argo tunnels - creates additional exposure
  4. Always use strong passwords and multi-factor authentication

What’s next?

Everything should be working now. If you want, you could consider adding Cloudflare for Teams for another layer of authentication. I tried briefly, but had some issues with the Home Assistant iOS app - but maybe you’ll have better luck! Cloudflare for Teams is free for up to 50 users.

Keep in mind that you are paying for traffic at USD $0.10 (10 cents) per GB, so you may not want to stream live video from cameras at all times through this.

If you want to add more applications to be exposed through the tunnel, you can edit the /etc/cloudflared/config.yml file - no additional cost outside of the bandwidth consumed. For example, if you want to serve out Proxmox, add this block between the Home Assistant line and the 404 line (again, changing parameters as needed):

  - hostname: proxmox.stringcheesefactory.com
    service: https://10.10.3.0:8006
    originRequest:
      noTLSVerify: true

So, here’s my full /etc/cloudflared/config.yml file:

tunnel: 9b28d7ca-823b-4706-a039-01df951e06a6
credentials-file: /home/ubuntu/.cloudflared/9b28d7ca-823b-4706-a039-01df951e06a6.json

ingress:
  - hostname: homeassistant.stringcheesefactory.com
    service: http://10.10.3.1:8123
  - hostname: proxmox.stringcheesefactory.com
    service: https://10.10.3.0:8006
    originRequest:
      noTLSVerify: true
  - service: http_status:404

After each change, you’ll need to make sure you restart the cloudflared service to apply the changes. And don’t forget to add another CNAME pointing to the UUID and cfargotunnel.

sudo service cloudflared restart

End

I hope this was helpful! Tweet @dcnoren if you have any questions or issues.