Learn how to connect your Home Assistant installation your Google Home device and Google Assistant integration, and how to do it securely with the help of Cloudflare Argo.

(Photo by BENCE BOROS)

Some time has passed since the last article in my series about Home Assistant. Truth is, I do not have an end in mind for wrapping up on this topic, so I will just keep adding my tutorials as soon as I achieve improvements over my configuration. If you haven't done it already, make sure to catch up on the previous episodes below:

Taking stock

Before we delve further in the topic at hand, I wanted to look back briefly and comment on my experience with Home Assistant, as it's now about 6 months since I installed it and connected my first devices. So far, it has been great: the tool has been very stable and is being constantly updated with new features.

There was a bit of a "drama" in early May when the Home Assistant team announced the end of support for the Docker approach (which is the one I use), but to their credit, they have been very receptive of the feedback from the community and revisited that plan. Home Assistant Container - a.k.a "Home Assistant Core on Docker" (a.k.a. "what I use") is now one of the four officially supported install methods. There is also plenty of community support for other methods and setups, which can be found here.

So, to wrap up this quick recap, I am fairly happy with Home Assistant so far!

Google Home and Google Assistant

The integration with Google Home / Google Assistant is an interesting one. In short, you can leverage the vocal recognition capabilities of your Google Home to give verbal commands directly to Home Assistant and create clever automation rules. Have you ever wanted to transform your living room in the deck of the USS Enterprise (NCC-1701)? Yeah, me too, and I can get one step closer to my dream with this hack.

However, there is a choice to make: there are two ways to integrate the Google Assistant. One is a paid subscription service offered by Nabu Casa: this is convenient but it's a paid for service. At 5$ per month, it  could be a good compromise for most people, and it goes to support the Home Assistant creators.

The other involves hooking up the integration yourself, getting your hands dirty in the Google Cloud console, and - and this is the most important bit - opening up your Home Assistant installation to the outside world. Yuck! Now you know why there is a hassle-free, paid version available as above 😅 In my article I will show two ways to achieve this, with the second one removing the need of opening any ports on your router.

Cloudflare

To recap, I needed a way to make the "open your HA installation to the world" part more palatable to my taste to be confident of using it. This is where Cloudflare enters into the scene - if you don't know Cloudflare be sure to check out some of my older articles:

In this case, I wanted to leverage the DDoS - SSL/TLS capabilities, as well as exposing my installation more securely. Turns out you can do two things:

  1. Open up your firewall ports and harden it so that only requests that are coming from the Cloudflare network are served.
  2. Alternatively, leave your firewall closed shut and install a Cloudflare Argo Tunnel in your network. This tool will automatically set up an optimised connection tunnel into the Cloudflare network, and from there expose an endpoint reachable from the outside world, which you can point to to acess your Home Assitant installation.

I have also tried a variant of the method (1) adding Cloudflare Access in front of your set up, but it becomes a bit fiddly to get everything in Home Assistant playing up nicely and, at the same time, integrate the Google Assistant API. I will revisit this in the future.

The best part of this setup is that all the above products are available for free (or free within specific tiers that we are very unlikely to exceed in this project). If you set this up, please verify whether the free or paid version works best for you.

It is worth clarifying that the free version of Argo, used in the setup option (2), does not come with guaranteed SLAs and the domain name used will change every time the tunnel is started. In short, it is not meant for running a production service. You may want to consider the paid version for a more seamless experience once you are happy with the trial set up.

The Plan

Prerequisites

In this guide, I will assume that the following are already in place:

  • You have a valid domain name that points to your Home Assistant installation. I covered this in Part 1.
  • You have configured your domain to use Cloudflare's nameservers - even easier if are using Cloudflare as your registrar.
  • Your Home Assistant installation is currently behind a firewall that blocks any outside access (except, perhaps, through a VPN).

With these in place, you are ready to proceed with the setup.

What we'll do next

To achieve our secure configuration, we will do the following steps next:

  1. Enable 2-factor authentication (if you haven't done so already) for your Home Assistant user.
  2. Run Nginx in a Docker container, and reverse proxy the traffic into your Home Assistant instance.
  3. Configure Origin Authenticated Pulls from Cloudflare on Nginx
  4. Open up a port on your router, forwarding traffic to the Nginx instance.
  5. Set up of Google Assistant as per the official guide and minding the set up above.

Up to this point, you will have implemented option (1). You can try it out and see if that works for you: at the end of this process, you should be able to control your Home Assistant compatible devices (in my case, lights) via voice commands on your Android phone or Google Home device!

If you are unhappy with that open port on your router (I was), then you will have to follow these further steps:

  1. Close the port on your router
  2. Remove authenticated origin pulls from your Nginx configuration
  3. Setup a cloudflared Docker instance, and forward the traffic to your Nginx server locally
  4. Update the Google Actions Console configuration to point to your Argo tunnel domain

After completing th above extra steps you will have an externally accessible Home Assistant configuration, via the Argo tunnel domain name, and without any open ports on your router. This is a safer stance than option (1) in my opinion.

Let's now walk through the above steps in some more details, and figure out exactly what we need to do.

Woman desiging UX diagram
Photo by Kelly Sikkema

2-Factor authentication

It is always good practice to enable 2-factor authentication for your online accounts. Home Assistant is already protected by its authentication system, and we want to enable 2-factor authentication on all the users that can log in from the Home Assistant web interface. This will raise the bar from anyone that manages to gain access to the external page and tries to brute force their way in.

Enabling this is simple, and can be done from the profile section of your accounts. Ensure that you have enabled it for all the accounts that require access. Further information can be found here - You can choose TOTP which works with any existing authenticator app on your phones such as Authy or Google Authenticator.

Nginx reverse proxy

As we're about to accept incoming external traffic into our Home Assistant instance, we could simply implement a port forward from our firewall to Home Assistant. However, this leaves us with little options in terms of control and filtering these requests. It is much better to stand up a reverse proxy in front so that we can perform any additional configuration and checks on the proxy - giving us a very flexible setup.

In our case, we want to configure a certificate (so that the incoming connections will be served over TLS), and we want to also configure authenticated origin pulls from Cloudflare. In other words, our Nginx server will only establish a connection if the requestor can supply the Cloudflare certificate as a credential, proving that the request comes from the Cloudflare network.

I will run a standalone Nginx instance on Docker. I will configure it to accept incoming connections on a specific port, and then forward these to Home Assistant's standard port 8123.

As I am using Synology - I will pull the nginx:stable-alpine image (see here for all images available) and configure it to map a few volumes:

  • /opt/certificates to the folder where my certificates reside
  • /etc/nginx to the folder including the nginx.conf file that I will use to configure it.

I will then map a port (for example 12345) to the container's own 443 port. The firewall will forward incoming requests to 12345.

Here is my nginx.conf configuration (using ha.example.tld and homeassistant:8123 as placeholders for you to adapt to your setup )

http {

    server {
        listen 80 default_server;
        server_name ha.example.tld;
        return 301 https://$host$request_uri;
    }

    server {
        listen 443 ssl http2;
        server_name ha.example.tld;

        ssl_certificate /opt/certificates/cert.pem;
        ssl_certificate_key /opt/certificates/privkey.pem;
        ssl_protocols TLSv1.3;
        ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
        ssl_client_certificate /opt/certificates/cloudflare-origin-pull-ca.pem;
        ssl_verify_client on;

        location / {
            proxy_pass https://homeassistant:8123;
            proxy_set_header Host $host;

            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }

        location /api/websocket {
            proxy_pass https://homeassistant:8123/api/websocket;
            proxy_set_header Host $host;

            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
    }
}
You may need to tweak the paths to the certificates to apply to your file and folder structure.

This is very much similar to the example in the Home Assistant Community Guide, with some tweaks, namely:

  • Enabling SSL and HTTP/2 for incoming connections
  • Specifying the preferred ciphers, and forcing the protocol to be TLS 1.3 - be sure to check out my TLS 1.3 overview article if you want to know more on this topic!
  • Adding the client certificate verification, you will need to download the Cloudflare Origin certificate and make it available to your Nginx instance at the specified location.

Make sure you update the placeholders ha.example.tld and homeassistant:8123 with your home assistant domain, and with the local address of your home assistant installation for the proxy.

Finally, verify that you have enabled the domain to be proxied via Cloudflare, enable TLS 1.3 and authenticated origin pulls in the Cloudflare Dashboard.

Proxied A record pointing the home assistant domain to your firewall / gateway
Proxied A record pointing the home assistant domain to your firewall / gateway
TLS 1.3 setting
TLS 1.3 setting
Full (strict TLS) - Make sure you have a valid certificate installed in Nginx!
Full (strict TLS) - Make sure you have a valid certificate installed in Nginx!
Authenticated origin pulls - so that Cloudflare presents the client certificate in the requests
Authenticated origin pulls - so that Cloudflare presents the client certificate in the requests

Router / Firewall port opening

Now that your Nginx docker instance is up and running and in front of Home Assistant, with a proper configuration, it is time to open up your router / firewall port. This step will vary depending on your router/firewall, but typically you will want to:

  • Create an alias for Cloudflare infrastructure IPs: this is an extra security step, which will make your firewall accept incoming connections on the open port only when they originate from a Cloudflare managed IP. This step is on top of the authenticated origin pulls configured above, but it helps in filtering out spurious or malicious requests even before they hit Nginx. You will find the latest list here.
  • Create a  NAT rule accepting a TCP connection on port 443 on your WAN interface, and forwarding the traffic to the Nginx instance you configured above. Make sure the request address is one of the IPs defined in the above alias. My firewall automatically creates a firewall rule associated with the NAT mapping, in your case you may need to do the two things separately.

That's it! You should now be able to hit the Home Assistant log in page from outside of your home network. All the requests will be coming from the Cloudflare network, and direct access to the webserver should be disallowed. A request without the Cloudflare client certificate will be met by an HTTP 400 error returned by Nginx, which you can see by attempting to hit your Nginx server with curl or wget locally.

Configure Google Assistant

Setting up the Google Assistant integration needs to be carried out in the Google Cloud console. This is already well documented on the official  Home Assistant guide for Google Assistant so I recommend you read it up there.

In short, you will create an Actions on Google project, point the action fulfilment URL to https://ha.example.tld/api/google_assistant, then configure the OAuth 2 integration for the authorization code https://ha.example.tld/auth/authorize and auth token https://ha.example.tld/auth/token. You will then be able to open your Google Home app on your phone and add the integration so that your Google Assistant will know about your fancy lights and gain the ability to control them.

UniFi Gateway and cables
(Photo by Thomas Jensen)

Now shut that open port ... with an Argo Tunnel

Right now, we have a Home Assistant web interface which is reachable from the outside world. For sure, the requests have been locked down to be served only via the Cloudflare network, which helps with preventing malicious access requests or bad actors. Even so, I'm sure a lot of you will feel itchy about that open port on the firewall.

To alleviate this problem, you can set up an Argo Tunnel using the cloudflared daemon. Instead of opening up a port and listen for incoming connections, you can spin up the daemon inside your network. It will then take care of creating a persistent tunnel with the Cloudflare network.

This tunnel is then exposed to the public via an accessible endpoint. In the trial version, this endpoint will be available at a randomly generated subdomain of trycloudflare.com. Requests sent to that endpoint will be forwarded through the tunnel to the cloudflared daemon in your network. We will then configure the daemon to forward these requests to our Nginx webserver.

Firstly, we need to update our Nginx configuration to remove the authenticated origin verification. In this setup, all the incoming connections to Nginx will be either from your local network (for example if you do some test requests) or from cloudflared. You already know that the requests originating from cloudflared will be coming from the Cloudflare network, so there is no need to validate it. Moreover, the daemon has no way to present the Cloudflare certificate since it is installed on your machine and has no access to Cloudflare's private keys, so the authentication would fail.

Then, we will run cloudflared daemon on Docker. I have used jakejarvis/cloudflare-argo and the configuration is really simple: you only need to set an environment variable TUNNEL_URL so that it points to your Nginx instance (remember to use the local address and port this time). If you configured it correctly, you will be able to see the randomly generated subdomain in your logs as seen below - although I removed the details for it.

If you choose to have a persistent, paid configuration, then you will be able to configure your own domain for the tunnel.
If you choose to have a persistent, paid configuration, then you will be able to configure your own domain for the tunnel.

Once the tunnel is up, you can verify that everything is working correctly by navigating to the tunnel URL and confirming that you can load the Home Assistant Web Login interface. The final step is to update your Actions on Google project so that the API and OAuth URLs are pointing to the Argo Tunnel.

The downside of the trial version of Argo Tunnel is that the randomly generated endpoint will change if you restart the docker image, so you will need to update it from time to time.

Conclusion

Congratulations! You have introduced Google to Home Assistant, and from now on they will be ready to serve you, the Master ... until the rise of the machines of course 🤖

Have you done this set up with your Home Assistant installation? Any tips or advice to share? Let me know in the comments section below.


This article is part of a series about Home Assistant: