sbjzn.com blog

Securely connecting Google Assistant to Home Assistant with minimal external exposure

Aug 14, 2024

Introduction

We wanted to control our smart lights using voice commands, and since we already have a Google Nest Mini speaker and a Home Assistant(HA) installation, integrating Google Assistant with HA seemed like the best solution.

However, I quickly discovered that setting up this integration requires that the HA instance is exposed to the internet. This is required because Google will do API calls directly to the HA instance. [ref]

Currently, the only service I have internet exposed is a WireGuard VPN, and exposing more things is something that I try to avoid.

I searched for alternatives, but did not find anything aside from using Nabu Casa, but I don’t want a cloud service to have full access to my Home Assistant instance. There’s also the cost to consider, Nabu Casa costs € 7.50 per month.

Finding a solution

Since there is a strict requirement for the instance to be internet-exposed, I explored what we can do to limit the exposure as much as possible.

Firewall configuration

While doing research earlier, I came across a blog post by Corey Braun where he had the same problem with HA, he has written a script to automatically fetch Google’s IP ranges and allow them through his OPNsense firewall.

Very clever idea of Corey, but I don’t have an OPNsense firewall at home, and the one I do have doesn’t have an API.

I decided to use UFW to block traffic on the host level instead, so I forked Corey’s script and added functionality to handle UFW as well. [google-api-ips-ufw on GitHub].

I’ve put the script in my crontab so that it runs on a schedule, if IP addresses are added to Google’s list, they are automatically added to UFW and vice versa if they are removed.

HAProxy

I wanted to restrict access further, so I decided to proxy the external traffic through HAProxy so that I can set ACLs on the requests. Looking at the Home Assistant documentation, The paths configured in Actions on Google are /api/google_assistant, /auth/authorize and /auth/token.

The /auth/authorize path is only accessed by your client when setting up the integration, so the only paths that Google actually needs to access are /api/google_assistant and /auth/token.

Below is the site configuration that I ended up with for HAProxy, the comments describe what each line does.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
frontend https
  # Bind to 443 with a letsencrypt certificate
  bind *:443 ssl crt /etc/letsencrypt/live/home-pxy.domain.tld/bundle.pem

  # ACL to check that the host header is correct
  acl host-check hdr(host) -i home-pxy.domain.tld

  # ACL to check that the path is one of the allowed ones
  acl path-check path -i -m beg /api/google_assistant || path -i -m beg /auth/token

  # ACL to check that if the request method is POST, Google only sends POST requests.
  acl method-check method POST

  # ACL to check for internal IPs
  acl ip-check src 192.168.20.0/24 192.168.10.0/24

  # Silently drop the request if either of the checks fail, but not for internal IP addresses
  http-request silent-drop if !path-check !ip-check || !host-check !ip-check || !method-check !ip-check

  # Forward the request if all checks pass, or if the ip is internal
  use_backend home if host-check path-check method-check || ip-check

backend home
  # Replace the Host header, required by my setup
  http-request set-header Host home.domain.tld

  # Setup backend
  server home home.domain.tld:443 check ssl verify none sni req.hdr(Host)

It allows clients on my local network to access the site at home-pxy.domain.tld without restrictions, which is required when authenticating Google Home to access HA, but all external traffic is restricted by path, request method and host header.

Summary

I think that with both the firewall and request filtering in place, we have reached a good security posture. We are doing far more than the recommended setup and I feel confident in exposing this through my firewall.

If you want to read further about how the firewall update script works, I recommend reading Corey’s blog post.

This is what the final solution looks like: Overview image of the Home Assistant setup