Intro #
One of my favorite things about Home Assistant is that it lets me keep control of my IoT devices off the internet. I was therefore disappointed when I found that connecting HA to Google Assistant requires allowing external access. I’d like to control my smart home devices with my phone’s assistant, but I’m not willing to completely open my HA instance to the world.
I found several others’ solutions to this problem on Home Assistant’s forums, but I ended up solving the issue in a different (and in my opinion better) way than the other solutions I found, so I’m making this post to document my process and hopefully help others who might have a similar goal.
Choosing an approach #
The most active thread regarding this topic on the Home Assistant community forums had several solutions suggested by users.
Some users had firewall rules only allowing a few CIDR blocks from Google’s SPF record to access their Home Assistant instance. While these users did seem to have success connecting Google Assistant to their HA instance, other users reported Google bots attempting to connect from IPs outside of these ranges. Using a manual whitelist like this is simply too unreliable. There’s no telling when the IPs Google uses to connect might change, and no way of knowing whether your list is actually up to date.
Other users were limiting access to their Home Assistant instance to any IPs associated with Google’s ASN. Where the previous user didn’t allow enough IPs, this solution cast the net too wide. Whitelisting the entire ASN allows Google’s APIs access, but also allows any Google Cloud Platform customers access, a very thin barrier to entry.
Neither of these solutions seemed to fit the bill for me, so I did some research on what IPs Google uses for their APIs, and found this Google support page. The page links to two lists of CIDR-notation IP blocks in JSON format:
Per the article, the list of IPs hosting Google APIs and services is the first list minus the second list. Using this information, I can make a firewall rule that only allows these IPs, thus limiting access to my Home Assistant instance as much as possible while still allowing it to connect to Google Assistant.
Creating a firewall rule #
Since the two lists of Google IPs are very long and updated frequently, updating this firewall rule would have to be automated. This means I’d need to write a script to not only get the IPs, but also add them to an alias which would be used as the allowed source IPs in a firewall rule. Fortunately, I use OPNsense as my firewall, which has an API through which I can update an alias with the contents of this IP list.
For actually getting the alias content, there is a Python module called netaddr which can subtract these two large lists of IPv4 and IPv6 CIDR-notation network blocks. Starting this project I knew almost no Python, but since I’ve been wanting to start learning it, this served as a great opportunity!
If you’d like to use the script I wrote, it is available at the github repository linked below. Even if you aren’t using OPNsense, it can write the list of IPs to a file, or print them to stdout for use however else you’d like.
Python script to create/update an OPNsense alias with the IPs used for Google’s APIs
Since the alias needs to be kept up to date, I created a cronjob to run this script daily on my linux server.
With the alias created and set to update, I next created a port forward on OPNsense which allows packets with a source matching the alias to reach my reverse proxy, Traefik. Traefik serves a valid SSL cert for my domain (a requirement for connecting to Google Assistant) and routes traffic from the forwarded port to my Home Assistant docker container.
Connecting with Google Assistant #
With the networking done, I followed Home Assistant’s documentation on connecting to Google Assistant. Within my Home Assistant configuration for the google_assistant integration I chose to only manually allow the few devices I wanted to control with my voice to connect to Google Assistant, further limiting access in the event that a connected Google account was compromised.
Once I was finished following the integration’s instructions in the docs I attempted to connect to Home Assistant in Google Home. On my first attempt I got an error saying Google “Couldn’t reach” Home Assistant. Within the Home Assistant logs, however, I found a login attempt with “invalid authentication” from one of the Google IPs in my OPNsense alias, which at least confirmed my networking was working, unlike what the Google Home error initially lead me to believe.
With the information gleaned from these nonspecific error messages, I eventually found my problem: The Home Assistant account I’d configured Google Home to use was set to only allow logins from the local network. After correcting this I was able to connect HA to Google Home successfully. With everything finally set up, I’m now able to control my devices with my voice via Google Assistant, all without making Home Assistant publicly accessible on the internet.