My Tailscale journey ends#
For the last year, I relied on Tailscale to have access to my self-hosted services from anywhere.
Tailscale is a zero-config mesh VPN built on the WireGuard protocol that creates a secure, encrypted peer-to-peer overlay network between your clients, in my case Proxmox LXCs, laptops and cellphones. It automates NAT traversal and key exchange via a coordination server, allowing direct connectivity across different networks without the need for manual firewall rules or port forwarding.
You have to setup the Tailscale client on each laptop and cellphone. In my home network, Tailscale operated as exit node on a separate LXC, so the whole network was reachable.
It worked great, but it has one big shortcoming. In the end it’s an active component, so the VPN has to be running on each device. This needs energy and at least on my Android, Tailscale was responsible for taking up to 15 - 20 % of the total power consumption.
Another solution had to be found.
Securely exposing self-hosted services without VPN#
The idea is to expose services to the bad internet, but to do it in a mostly secure way. Sounds impossible?
Every service exposed is potentially vulnerable to security issues. So having everybody able to connect and probe it is dangerous. But what if the service is reachable from the internet, but only for a few specific clients?
Meet mTLS (Mutual TLS): The underlying TLS connection will only be created, if the client authenticates itself with a valid certificate of our certificate authority.
As this is passive and only sent when opening the page, it costs no power or traffic.
If the client do not have a valid certificate, the whole connection won’t be created. A possible attacker can not even see which service is running.
The downsides:
- You have to create certificates
- You have to deploy them to every device
- Necessary to enforce them by using a reverse proxy like caddy / nginx
- It works in the browser, but dedicated apps must support mTLS
How I secured my selfhosted services with mTLS#
Before you start, you should already have running setup with the reverse proxy of your choice. I chose caddy, because it is fast and really easy to configure.
Create a CA#
I created a root CA by using the Smallstep CLI. You could also do it by using openssl etc. directly, but smallstep-cli makes it easier.
paru step-cli
step certificate create "myhomelab.org CA" ca.crt ca.key --profile root-ca --no-password --insecureThis creates your ca.crt and ca.key. I decided to create them without passwords (option --no-password), because I upload them to my encrypted password manager and only download them if needed. For more security if laying around on a harddisk, a password should be set.
Create client certificates#
Now you are able to create client certificates:
step-cli certificate create "Testuser" client.crt client.key \
--profile leaf --not-after=876000h \
--ca ca.crt --ca-key ca.key \
--no-password --insecureWhat is important is that you set the expiry by adding --not-after, otherwise the certificate will only last a few days.
Now we are converting it to .p12 format, because some apps only accepts this format.
step-cli certificate p12 client.p12 client.crt client.key --ca ca.crtVoila, we now have our client certificates, ready to rollout.
Enforce mTLS in reverse proxy#
Now it is time to secure the service. For this, we require a valid tls client certificate by adding this to our Caddyfile:
/etc/caddy/Caddyfile#
# Define snippet for use in directives
(require_mtls) {
tls {
client_auth {
mode require
trusted_ca_cert_file /etc/caddy/ca.crt
}
}
}
importantservice.myhomelab.org {
import require_mtls
reverse_proxy 192.168.0.100:8000
}The Root CA certificate file ca.crt must be uploaded to the configured path, in my case /etc/caddy/ca.crt.
Now it is time to check, the service should not be reachable anymore, because caddy denies the connection setup.
Setup clients#
The client certificates now have to be deployed and used in the browser of your choice. In Firefox the import is found at Settings - Privacy & Security - Certificates.
After installing the client certificate there, a popup should appear when opening the website, where you can choose your client certificate.
It has to be set one time, after this you can use the website as usually.
Final thoughts#
I am exposing my most critical services secured with mTLS for about 4 weeks now and in my opinion it is the far better solution than setting up a VPN.
To summarize:
- It is passive, no power consumption on clients
- Needs no client software
- No attacker or unknown client is even able to probe or see your service
- Independent of external services like Tailscale.
