This is my blog now

How To: Deploy Services with TLS Certificates in a Tailscale network

Perhaps you've been in this situation before. You have a home server which hosts an bunch of stuff. Possibly in VMs, containers, what have you. You want a way to address these servers from elsewhere so you configure a reverse proxy that routes to each of these based on hostname. Personally, I have Jellyfin, Adguard Home, Uptime Kuma and a few other things deployed on the same machine and wired-up this way.

Everything works fine but every time you navigate to one of these services your browser gives you a warning about unsecured traffic. Furthermore, you'd like to access your stuff from outside your home network. At the same time you're reluctant to expose your personal stuff to the internet which would be the most straightforward solution to this (and I've done it before).

What to do?

I'll sketch a broad solution to this issue without going into too much detail because there are many moving parts in all this and your specific setup will almost definitely vary. Heck, even my setup varies.

One part of the solution is obviously VPN. You can set one up to access your home network without inviting anyone else in. For the purpose of this article it doesn't matter how exactly you do this. You can go ahead and configure some Wireguard yourself, maybe your router even supports this out of the box (mine does) or maybe you go with a service like ZeroTier or Tailscale (which is what I use) or maybe you self-host such a solution. Doesn't matter.

Side note: I am aware that Tailscale supports TLS certificates for your Tailnet but this doesn't help you, if you run multiple things on the same node.

Going forward I'll assume you have some sort of VPN solution set up.

Since you're reading this you're probably aware of "Let's encrypt" which is the default way to get TLS certs nowadays, if you're a private person that is. Usually, you'd install certbot, which is their ACME-client, and use it to have a cert issued to you. By default this uses the HTTP-01 challenge during which the remote server sends a token to your webserver which stores it on disk so the remote server can verify it's present and issue you a cert. This serves to prove that you control the server the domain you want a cert for points to.

For obvious reasons this won't work, if you've got your server behind a personal VPN but there is a solution. The certbot client supports another type of challenge which is the DNS-01 challenge. During this the remote server sends a token to the ACME-client which it uses as the value for a TXT record for the domain you want a cert for. This way you prove that you control DNS for the domain in question. For this to work in an automated fashion you need a domain registrar that has an API you can use. This is pretty common but it might be worth having a look at, if you're in the market for domains. certbot has a bunch of plugins for interacting with the APIs of various registrars. If you can't find an official one, there might be a custom one by the registrar itself (as is the case for porkbun where my domain lives).

If you don't want to use certbot manually or script it somehow, or don't want to deal with the reverse proxy setup, there is a project that does all of this for you: nginx proxy manager. This can be deployed as a container and offers a nice web UI in which you can configure everything with ease. If you can't or don't want to run Docker (or Podman), you can still do all of this yourself. I've recently set up a FreeBSD machine with which I want to do this.

Now that all the pieces are together let's recap how it all fits together:

  1. Set up the services you want to run, a VPN solution and API access for your registrar
  2. Create DNS records for each service to point at the Tailnet IP address of the host it runs on (personally I use a subdomain for each service)
  3. Install certbot and the relevant plugin for your registrar
  4. Get a cert for your domain(s). The command might look like this:
    certbot certonly \
    --non-interactive \
    --agree-tos \
    --email <your-email-address> \
    --preferred-challenges dns \
    --authenticator dns-porkbun \
    --dns-porkbun-key <your-porkbun-api-key> \
    --dns-porkbun-secret <your-porkbun-api-secret> \
    --dns-porkbun-propagation-seconds 60 \
    -d "*.example.com"
  5. Install the cert on your reverse proxy and configure it to use it

That's it. A few remaining notes:

As indicated above, nginx proxy manager does steps 2-4 from above for you, if you don't want to. Automatic renewal is still not possible, though, you'll have to periodically get a new one.

I hope this helps! At least I am very glad to be rid of annoying browser warnings for my personal stuff!

How To, Linux

⬅ Previous post
How playing with Duplo led to some recreational maths

Next post ➡
Review: Sea of Stars