Adventures in mTLS

For a while now, I’ve followed a pretty strict rule: public services are public, and private services are private.
For me, that meant that the only things on my network that were exposed to the public internet are my blog and public websites. Private services, like Home Assistant or Navidrome, are only accessible from my local network (or remotely via VPN).

Today I changed that a bit…

I’ve now added Home Assistant to my external-facing proxy. This certainly sounds like it violates my personal security rule, but it’s actually a bit more reasonable than it sounds. Nginx is configured to require a mutual TLS exchange before it will allow a connection to Home Assistant.

Mutual TLS

Most every website and service you connect to these days uses Transport Layer Security (TLS). This is a system that allows your browser to validate the server you’re connecting to. When you visited this blog, for example, your browser used TLS to cryptographically validate that a) the blog post you received actually originated from blog.k3can.us, and that b) the post has not been changed or tampered with along the way. Mutual TLS takes this a step farther and allows the server to cryptographically validate that the client is really who they’re supposed to be, too.

For the rest of the world, attempting to connect to my Home Assistant instance will simply return an nginx error; they never even make it to a log-in screen.
Only devices with my personal TLS certificate installed can fully establish the connection.

screenshot showing an nginx 400 error

This keeps Home Assistant safely protected from strangers, while still allowing me access it from any where, even without a VPN.

The process is somewhat time consuming, but not overly complicated. The always amazing Apalrd’s Adventures has a great how-to video on the topic, and Dominik Britz has a blog post worth reading as well.

If you have any local services you want to access over the internet with a VPN, but still want to keep private, mTLS can be a great solution.