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:
- Set up the services you want to run, a VPN solution and API access for your registrar
- 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)
- Install
certbot
and the relevant plugin for your registrar - 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"
- Install the cert on your reverse proxy and configure it to use it
That’s it. A few remaining notes:
- The
--authenticator
option is incompatible with options such as--nginx
so you’ll have to install the cert to the proxy manually. - You can create wild card certs this way (which is not possible with the HTTP-01 challenge) which is why I use subdomains so I can use the same cert for everything
- Automatic renewal will not work with this setup, you simply request a new cert and put it in place. This can (and should) be scripted in a cron job or similar.
certbot
is a Python application. It’s packaged for a lot of systems so installation should be easy enough but the same may not be true for the API plugin. Take care that you install both in the same place. For example, porkbun has a PyPI package so I installed it viapip
but I had to do so asroot
because otherwise it would be installed in the user’s home directory as opposed to the repo package ofcertbot
.
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!