Skip to main content

        How a holy trinity of device control, internet security and DNS filtering controls most of my network

Mesh, Exit, Resolve: A Tailscale Setup for Paranoids and Homelabbers

How a holy trinity of device control, internet security and DNS filtering controls most of my network

Introduction

Okay okay, the title of this blog post might be a bit much, but let me explain.

For years I ran ProtonVPN on my personal clients, but I had a few issues with it.
Neither could I use different locations for apps on my mobile phone, nor was it easy to use on my Linux machines cause of the missing CLI and lack in the GUI.
The Net-Shield was also horrible in doing it’s job.

After that I tried Mullvad, good CLI but otherwise very similar problems.

And both of these had another big issue for me. I had to disable them every time I needed tailscale for remote access.

So what was my solution?

For a long time, I wasn’t sure and just lived with all of that because I thought I had no other choice.

You can probably relate to how big my grin was when I saw that Tailscale had an Mullvad VPN addon I could by for just 5 bucks a month. This allowed me to easily access my homelab while having a secure connection over one of Mullvads Servers.

And now to the star of the show, ControlD.

It’s a DNS filtering system that most people just use to block ads, but it can do so much more.

I would say if you just want to block ads, use NextDNS or Quad9 as those are way simpler.

But I also use it for location redirects for some services.
Okay fine, I do much more then just that, but that doesn’t really matter here as I just want to explain how I fix my most annoying problems.

But back again to tailscale, a few months ago from writing this, they announced their new partnership with ControlD which lets you implement it directly in your tailnet.

So that’s it, my holy trinity of Tailscale, Mullvad and ControlD.

Ughh, that was a long introduction, so lets see what the table of content looks like so you can easily jump to the topics that interest you the most.


Table of content

  • Tailscale (Free)
    • Pocket-ID to log into your tailnet
    • Adding machines with Ansible
    • SSH made easy
    • Who needs a reverse proxy when you have services
  • Mullvad (Paid Tailscale addon)
    • How to set it up
    • Adding machines
    • Use on mobile devices (IOS as example)
    • Use on Linux (I use arch btw.)
  • ControlD (Full-Control Plan)
    • Add it to your tailnet
    • My profile settings
    • The power of services and how I get the benefits of YouTube Premium without paying
    • Types of Rules and ways to ditch VPN blocks
    • Extra profile options
  • Final verdict

Tailscale (Free)

A free VPN that lets you connect to your devices with ease

Pocket-ID to log into your tailnet

Let’s start at the basics, and that is how to log into your tailnet.
For most users, I recommend just using GitHub or any other form that is already available.
Note that you cannot sign in with your e-mail by default.

For those who want privacy and control, I recommend setting up Pocket-ID on your own server or a rented virtual private server (VPS).

I choose that route, so first we need to buy our VPS server.

I choose Ionos since I already have a few servers there.
A basic option should be sufficient; 4 or maybe even two GB of RAM should be enough.

Secure your server before installing sensible content

The server will be facing the public internet, so make sure to secure it properly and update it regularly. A domain and reverse proxy is recommended.

Perfect, now let’s install Pocket-ID and set it up.

There are many ways to do so. Bare Metal, Docker and Podman all look fine.

For my own sanity I choose Podman since I know this tool quite well and have a rootless container thanks to it.

But even when choosing Podman, there are a few options for us.
The one I choose is via Quadlets, which lets you create systemd units out of the containers.
But a simple podman run or podman compose command can be used just like with docker.

The following is an example quadlet file called pocket-id.container, which can be placed in your home dir under /home/your-user/.config/containers/systemd/

[Container]
Image=ghcr.io/pocket-id/pocket-id:v2
PublishPort=1411:1411
Volume=pocket-id:/app/data:Z
Volume=custom-path/encryption/key:/app/data/encryption/key:ro,Z

HealthCmd=/app/pocket-id healthcheck
HealthInterval=1m30s
HealthTimeout=5s
HealthRetries=2
HealthStartPeriod=10s

Environment=APP_URL=https://auth.domain.tld
Environment=ENCRYPTION_KEY_FILE=/app/data/encryption/key

Environment=TRUST_PROXY=false
Environment=MAXMIND_LICENSE_KEY=

[Service]
Restart=always

[Install]
WantedBy=default.target

Before activating this new service, we first need to add some extras before we can start.

Generate a new encryption key with

openssl rand -base64 32

And place it in your custom path for it.

Then reload the systemd daemon systemctl --user daemon-reload and start the container with the systemctl --user quadletname.service command

We have now configured pocket-id, next step is to assign the domain. I cannot tell you the steps and clicks for your own domain provider, but with cloudflare we just need to create a new A and AAAA record and point them to both the IPv4 and IPv6 addresses.

Then, add the domain to your reverse-proxy and assign the IP address.

Open the domain and you will be greeted with the account creation page. Make sure that you have a security key ready or that your password manager extension accepts passkeys. ![[Pasted image 20261110171718.png]]

I think it is fine to use your actual user as the admin here since both accounts would only be accessible via passkey anyway.

You’ll then be redirected to the My Account settings where you can customize everything to your liking. Add a second Passkey (here both a physical and digital passkey will be a good addition) and you’re all set when it comes to your account.

Navigate to OIDC Clients under Administration and create your tailscale app.

This below is an example on how it is set up for me, you’ll see the Client ID too. ![[Pasted image 20261110172106.png]]

It is important that you assign a Group and add credentials.

When done, navigate to https://login.tailscale.com/start and choose Sign up with OIDC. Enter the E-Mail with the same domain as your pocket-id domain and click on Get OIDC Issuer.

Then, just add the Client ID and the Client secret from pocket-id and click on Sign up with OIDC, if done right, everything should work.

If you encounter problems, I recommend watching Use a Custom Oidc and Passkeys to Log in to Tailscale with Pocket ID to make sure you have done everything right.

Congrats, you now have a working tailscale instance with a login you manage yourself.

Adding machines with Ansible

STILL IN TESTING; BLOG POST WILL GET UPDATED WHEN DONE

SSH made easy

The thing is, well all know the pain of having multiple ssh keys and perhaps even managing an ssh config file, but what if we could make this easier?

Introducing Tailscale SSH.

I like it because it is, for me, the easiest way to ssh into my machines. You can configure how and who can access which machines in Tailscale but the default settings are fine by me.

Make sure you have turned on Magic DNS in Tailscale. Then, hop into the machine you like and execute sudo tailscale set --ssh. That’s it. Now you can always type the ssh machine-hostname and tailscale will ask you to verify in your browser once per session. For me one of those sessions lasts like a day. After doing so, you are now logged in to your machine, but not only this one.

With this one session you can now automatically log into every machine you have set the ssh flag without typing in passwords or anything else. And the best part? It is just as secure if not more secure then normal ssh with your keys.

Who needs a reverse proxy when you have services

Gonna be honest here. Tailscale services are not a full replacement of an reverse proxy. It makes it easy to assign domains to your services and have https, but for special things you might still need a reverse proxy. Pocket-ID cannot secure all your services, that is where something like tiny-auth comes in. You can still put it behind tailscale services but you are gonna need a normal reverse-proxy.

Anyway, I just wanted to show you this really cool feature.

In Tailscale, go to the Service Tab under machines (your menu might be different based on when you view this post). Click on Create Service and fill everything out. I recommend adding a Display Name, as this makes things much easier for you. Port can be 443 since this is what the service will be listening too. ![[Pasted image 20261110184407.png]]

Based on the service/subdomain name you gave, you’ll see a part of a command below hosts. Use this instead of what I present in the following example and make sure the server is tagged.

SSH into your server with tailscale ssh when set up and see the port of your service that is going to be served and execute the following commands based on what method the service uses.

Adding a new Service

When trying to add a new service after creation in the Tailscale Admin Console, use a command like the following to activate it on the remote machine. After that, you will need to approve it.

sudo tailscale serve --service=svc:svc --bg --https=443 http://127.0.0.1:8080

Note that https+insecure://127.0.0.1:8080 is needed when the service has a self-signed certificate.

Then you’ll need to approve it in the admin console of tailscale. It will look something like this: ![[Pasted image 20261110185414.png]]

When done, try to open the new domain. A timeout is normal and when that happens, refresh the site and wait again while the TLS certificate gets created.


Mullvad (Paid Tailscale addon)

How to set it up

This might be the easiest part you’ll see here. In Tailscale, click Billing under the Settings Tab, click on Add-ons and select Mullvad. It is important to know that you do not need as many Mullvad clients as you have actual clients in your tailnet as those get assigned manually. For example, 1PC (Personal Computer) + 2NB (Notebook) + 1MP (Mobile phone) + 10 SRV (Server) equals 14 devices in total. If you buy the Mullvad Add-on for 5 devices, you have more then enough since you won’t assign the Add-on to your servers.

Fill out the information needed and complete the payment

Adding machines

In Settings, click on General and then under the Mullvad VPN section on configure. Here you can add the devices you think need the Add-on like your PC, NB and MP as stated in an earlier example.

They can now use Mullvad VPN as an exit node in Tailscale.

Use on mobile devices (IOS as example)

Open the Tailscale App on your Smartphone. You’ll see the field EXIT NODE on the top. Click on it and select Location Based. Now choose the server you’d like to connect to and it’s done. Verify Mullvad works by opening a page like ip.me and confirm that your location has been changed.

Use on Linux (I use arch btw.)

I tested this on Arch Linux, but it should work on other distros too.

Simply execute the following command to see the list of available servers.

tailscale exit-node list

You can then select the correct one with --exit-node=yourMullvadServer.ts.net.

Set accept routes and lan access with Mullvad VPN Add-on

In the following I give you an example that not only activates the Germany, Berlin server from Mullvad, but also activates routes and LAN access in your local network.

tailscale set --accept-routes --exit-node=de-ber-wg-003.mullvad.ts.net --exit-node-allow-lan-access --ssh

ControlD (Full-Control Plan)

Add it to your tailnet

After creating your first profile, go to Endpoints in the left pannel and click the plus at the top right. Scroll all the way down until you see Tailscale in the Endpoint Type. Choose a name and the Enforced Profile afterwards.

When done correctly, you now see a list of the Resolvers. Copy the resolver ID and go back to Tailscale.

In the DNS Tab under Network, scroll down to the nameservers and click on Add nameserver. Choose ControlD and paste the resolver ID.

Your devices should now see and use ControlD, no matter their type.

My profile settings

I have roughly half of the default filters activated plus some of the third-party-filters to block even more ads and trackers. What you choose is up to you, but I recommend to block New Domains for at least a week. You can always create exceptions in the Rules if you need to access one. Below are my profile settings: ![[Pasted image 20261110191831.png]]

The power of services and how I get the benefits of YouTube Premium without paying

Under the Services tab, you can choose between pre-selected Apps that not only block/allow based on the domain, but also on the App itself.

And here is where the power comes in. If you have selected the highest plan, you can make use of proxy servers. I want to have YouTube without adds but still have my phone VPN in the country I live in. So I create a new Service for YouTube and choose for example Albania as the proxy. Reload the YouTube app on your phone and you should see no ads.

Besides the proxy, you can of course block access to specific Apps. If you want your kids as example to have access to Instagram but not TikTok, the general social media filter would not work. You can still activate it tho and just allow the Instagram Service to be allowed as an exception.

Types of Rules and ways to ditch VPN blocks

We already discussed blocking, allowing, or redirecting via proxy in the Services part. You can do the same thing via domain here in the Rules Tag but with just a domain is the service isn’t there yet.

When creating folders, you can choose normal ones but also the following:

  • Control D Bypass
    • As the name suggest, it bypasses the ControlD filters and gives you a direct connection
  • Do Not Log
    • Excludes the traffic in the logs
  • Website Blockers and and bypassers
    • This is not one for itself, but you can choose them as Folder Rules

When I was trying to convert my websites into Codeberg Pages via Forgejo Actions workflow, I had the problem at first that I tried to access the sites to early. It was not up and ControlD caches such a response for 30 Minutes.

This is where the last of my use cases comes in. In such a case, I create a new rule for the domain I want and redirect it to an IP by clicking on redirect, then IP or Hostname and enter the IP it would listen to. In the case of Codeberg that is 217.197.84.141. It is also usefull to set an expiraction to this rule by clicking on the clock next to redirect and choosing the time it should expire.

Extra profile options

Not much to explain, but I like to activate the AI Malware Filter and set it to aggresive ^-^


Final verdict

It may look like a lot of work, because it is. No one said it would be easy. I myself had to click through multiple videos and documentation until I had everything done right.

But that doesn’t mean it wasn’t worth it.

I LOVE this setup and can recommend it to anyone with lets say an intermediate knowledge of IT.

It is elegant yet powerful and does the job for me better then I could’ve ever imagined.

Is it necessary? Of course not. We are homelabbers, nothing we do is really that important. But it’s fun and a one time job that’s worth the hours you may spend.

I hope you learned something here.

One last thing I want to say. I changed a bit how this Blog works (you’ll see this in the new Blog I am currently testing) so this post should automatically be published on Mastodon. If you have an account there, I would love if you commented beneath it. Your comment will be displayed under this post then here on my blog.

And as always… Stay private. Stay root.


Photo by Rohan on Unsplash

Comments

Comments are replies to this post on Mastodon. Reply there from any Fediverse account and your comment shows up here.

Loading comments sends your IP address to techhub.social (privacy policy).