DEV Community

pqp.vc
pqp.vc

Posted on

Startup setup - Part 1: VPN

I've been using Tailscale within its free tier and it has been an amazing experience so far, 10/10 would recommend it. I literally have zero complaints to do about it.

But I have a problem with the idea of having someone else "own" the VPN I'm using throughout my company and my projects, it doesn't sound safe at all from a business perspective. Most likely that's just me being paranoid, I am aware of that, but also why not host my own?

Headscale is basically a self-hosted alternative for it. It's easy to set up and is compatible with Tailscale CLI, which is great since I'm used to that. And on the plus side, since I'll be self-hosting it, I can also configure it to my liking.

It will unfortunately be a 2-part post, since I'll need to configure other things to get to SSL configuration I'm looking for.

Let's get that started.

Context

I launched a new cloud server on Hetzner and configured the domain vpn.example.com to its IPv4.

Keep in mind that everything for this post was done on a temporary server and not on my production one, so it's safe for me to leave the access tokens here because they don't exist anymore on any of my servers.

Installation

You can run Headscale using Docker, as described in their own documentation here. I'll run most my services using Docker, but the VPN itself I'll install and configure manually. And I think this has to be the first step in the process, so that's I'm doing this post first.

The official docs for the installation are here, I'd definitely recommend following that page if you want to set it up yourself. This post of mine is more like a journal of me following that same guide and configuring the server as I think it makes sense for my use case.

First, let's download the installer:

root@hz-headscale-tmp-1:~# export HEADSCALE_VERSION="0.23.0" \
  && export HEADSCALE_ARCH="arm64" \
  && wget --output-document=headscale.deb   "https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_${HEADSCALE_ARCH}.deb"

<...redacted for brevity..>

2024-09-28 12:24:54 (189 MB/s) - ‘headscale.deb’ saved [17022910/17022910]

Enter fullscreen mode Exit fullscreen mode

Then installing it, enabling it on start up, and removing the installer:

root@hz-headscale-tmp-1:~# apt install ./headscale.deb \
  && systemctl enable headscale \
  && rm ./headscale.deb

<...redacted for brevity..>

Created symlink /etc/systemd/system/multi-user.target.wants/headscale.service → /usr/lib/systemd/system/headscale.service.
Enter fullscreen mode Exit fullscreen mode

Configuration

Once installed, I edited the configuration file:

vim /etc/headscale/config.yaml
Enter fullscreen mode Exit fullscreen mode

These are the parameters I configured in this file:

server_url: https://vpn.example.com:8080
listen_addr: 0.0.0.0:8080
acme_email: "<my real email>"
tls_letsencrypt_hostname: "vpn.example.com"
dns:
  base_domain: node.vpn.example.com
Enter fullscreen mode Exit fullscreen mode

Some of these settings are temporary and will be revisited, but for the initial set up I think that's ok. I'm also very interested in the dns:extra_records: part but I'll leave that for later.

For now, let's start the server and confirm it is running:

root@hz-headscale-tmp-1:~# systemctl start headscale
root@hz-headscale-tmp-1:~# systemctl status headscale
● headscale.service - headscale coordination server for Tailscale
     Loaded: loaded (/usr/lib/systemd/system/headscale.service; enabled; preset: enabled)
     Active: active (running) since Sat 2024-09-28 12:34:57 UTC; 1min 3s ago
   Main PID: 18959 (headscale)
      Tasks: 7 (limit: 4433)
     Memory: 13.7M (peak: 14.1M)
        CPU: 243ms
     CGroup: /system.slice/headscale.service
             └─18959 /usr/bin/headscale serve

Sep 28 12:34:57 hz-headscale-tmp-1 headscale[18959]: 2024-09-28T12:34:57Z WRN
Sep 28 12:34:57 hz-headscale-tmp-1 headscale[18959]: WARN: The "dns.use_username_in_magic_dns" configuration key is deprecated and has been removed. Please see the changelog for more details.
Sep 28 12:34:57 hz-headscale-tmp-1 headscale[18959]: 2024-09-28T12:34:57Z INF No private key file at path, creating... path=/var/lib/headscale/noise_private.key
Sep 28 12:34:57 hz-headscale-tmp-1 headscale[18959]: 2024-09-28T12:34:57Z INF Opening database database=sqlite3 path=/var/lib/headscale/db.sqlite
Sep 28 12:34:57 hz-headscale-tmp-1 headscale[18959]: 2024-09-28T12:34:57Z INF Setting up a DERPMap update worker frequency=86400000
Sep 28 12:34:57 hz-headscale-tmp-1 headscale[18959]: 2024-09-28T12:34:57Z WRN Listening with TLS but ServerURL does not start with https://
Sep 28 12:34:57 hz-headscale-tmp-1 headscale[18959]: 2024-09-28T12:34:57Z INF Enabling remote gRPC at 127.0.0.1:50443
Sep 28 12:34:57 hz-headscale-tmp-1 headscale[18959]: 2024-09-28T12:34:57Z INF listening and serving gRPC on: 127.0.0.1:50443
Sep 28 12:34:57 hz-headscale-tmp-1 headscale[18959]: 2024-09-28T12:34:57Z INF listening and serving HTTP on: 0.0.0.0:8080
Sep 28 12:34:57 hz-headscale-tmp-1 headscale[18959]: 2024-09-28T12:34:57Z INF listening and serving debug and metrics on: 127.0.0.1:9090
Enter fullscreen mode Exit fullscreen mode

Looks good to me. Let's try connecting something to that. I'll first create a user called infra since I'll be connecting some infrastructure nodes to it later, and will also register a reusable preauthenticated key for these nodes.

root@hz-headscale-tmp-1:~# headscale users create infra
User created
root@hz-headscale-tmp-1:~# headscale preauthkeys create --user infra --reusable --expiration 7d
2024-09-28T12:37:05Z TRC expiration has been set expiration=604800000
c14c099b55aaaa3851aa5bafa8f91f6406f0161aa0059981
Enter fullscreen mode Exit fullscreen mode

That outputs a hash that is the authorization key. Keep that hash somewhere and we'll use it to test with a different machine:

root@hz-sample-node-1:~# curl -fsSL https://tailscale.com/install.sh | sh
Installing Tailscale for ubuntu noble, using method apt
<...redacted for brevity..>
Installation complete! Log in to start using Tailscale by running:

tailscale up
root@hz-sample-node-1:~# tailscale up --login-server https://vpn.example.com:8080 --authkey c14c099b55aaaa3851aa5bafa8f91f6406f0161aa0059981
root@hz-sample-node-1:~# tailscale status
100.64.0.1      hz-sample-node-1     infra        linux   -
Enter fullscreen mode Exit fullscreen mode

Results

Well, now I have a basic instance of Headscale installed to which I can connect servers and client as needed. It's not a final installation yet, but it's a good start.

Final considerations

Everything I pasted here from the terminal was done on a temporary server. I have since them reinstalled everything a newer and more definite server, so any token seen in this post is invalid for sure.

Like previously mentioned, I'll have at least one more post regarding Headscale since I'll be revisiting some parts of the configuration at a later moment. But for now it works as a starting point.

Top comments (0)