[Headscale] FOSS implementation of the Tailscale control server for VPNs

Headscale for YunoHost

Integration level Working status Maintenance status

Install Headscale with YunoHost

This package allows you to install Headscale quickly and simply on a YunoHost server. If you don’t have YunoHost, please consult the guide to learn how to install it.

Overview

An open source, self-hosted implementation of the Tailscale control server.

What is Tailscale

Tailscale is a modern VPN built on top of Wireguard. It works like an overlay network between the computers of your networks - using NAT traversal.

Everything in Tailscale is Open Source, except the GUI clients for proprietary OS (Windows and macOS/iOS), and the control server.

The control server works as an exchange point of Wireguard public keys for the nodes in the Tailscale network. It assigns the IP addresses of the clients, creates the boundaries between each user, enables sharing machines between users, and exposes the advertised routes of your nodes.

A Tailscale network (tailnet) is private network which Tailscale assigns to a user in terms of private users or an organisation.

Design goal

Headscale aims to implement a self-hosted, open source alternative to the Tailscale control server. Headscale’s goal is to provide self-hosters and hobbyists with an open-source server they can use for their projects and labs. It implements a narrow scope, a single Tailnet, suitable for a personal use, or a small open-source organisation.

Features

  • Full “base” support of Tailscale’s features
  • Configurable DNS
  • Node registration
    • Single-Sign-On (via a dedicated Dex app)
    • Pre authenticated key
  • Taildrop (File Sharing)*
  • Access control lists*
  • MagicDNS
  • Support for multiple IP ranges in the tailnet
  • Dual stack (IPv4 and IPv6)
  • Routing advertising (including exit nodes)
  • Ephemeral nodes*
  • Embedded DERP server*

*untested, cf. “Notes for admins” below.

:arrow_up: Description adapted from Headscale’s README. See link to upstream app below.

Documentation and resources

Notes for admins :warning:

Tweak Headscale’s configuration in /var/www/headscale/config.yaml. Restart the Headscale service after your alterations.

All features might not be compatible with YunoHost.
Feel free to experiment and report your use cases in YunoHost’s forum and chatrooms!

Some technical notes:

  • Internal DERP server is not enabled
  • The server is allowed to use the DERP servers operated by Tailscale
  • The database used to store user and nodes information is SQLite. It should be fine for YunoHost’s current small-scale self-hosting mindset.
  • Headscale will push DNS servers to its clients. The configuration includes FDN’s open resolvers. (French Data Network, associative ISP)
  • The app is only a VPN server, and does not include the Tailscale client. Install it manually, cf. Headscale’s documentation.

To create users and connect clients, refer to Headscale’s documentation.
To call Headscale’s program, use sudo -u bash /var/www/headscale/headscale followed by its parameters.

Developer info

Please send your pull request to the testing branch.

To try the testing branch, please proceed like that.

sudo yunohost app install GitHub - YunoHost-Apps/headscale_ynh at testing --debug or sudo yunohost app upgrade headscale -u GitHub - YunoHost-Apps/headscale_ynh at testing --debug

More info regarding app packaging: Introduction to packaging | Yunohost Documentation

4 Likes

:warning: The “exit node” feature has not been tested (as in, what anyone might expect from a VPN). Feel free to report your tests and findings to help at minimum document it or at best pre-configure the app for it.


Experiments log on MagicDNS

2023-06-13
Demanding to use a domain registered on YunoHost as base domain seems to be an error.

For example I had installed Headscale on headscale.domain.example and chose the same for the base domain. Like that, a Raspberry Pi called rpi, registered to a user can be accessed at rpi.user.headscale.domain.example.

One could imagine providing services restricted to the VPN, and register rpi.user.headscale.domain.example as a domain in the RPi. However, since headscale.domain.example is already managed by the VPN server, the user’s browsers might have already registered its HSTS settings, blocking the usage of self-signed certificates provided by the RPi.

:arrow_right: Solution, make the base_domain installation parameter a free text form. I tried with hs.lan (hopefully a fake TLD that will remain free…)… and…

tada!

To be fixed in Headscale next release. Feel free to share your thoughts.


Service reboot is slow

2023-06-13: the service log shows multiple Error listing users error="sql: database is closed"when the service is being… slowly… rebooted. To be investigated.

1 Like

Great!
Having headscale in Yunohost makes VPN more flexible and secure! :grinning:

I also experience the slow reboot and Error listing users error="sql: database is closed"

It is not possible to register a node to headscale. sudo tailscale up --login-server=https://hs.<mydomain>.de --accept-routes hangs forever with /var/log/syslog listing:

Jun 17 16:50:42 aspire tailscaled[212511]: control: LoginInteractive → regen=true
Jun 17 16:50:42 aspire tailscaled[212511]: control: doLogin(regen=true, hasUrl=false)
Jun 17 16:50:42 aspire tailscaled[212511]: Received error: multiple errors:
Jun 17 16:50:42 aspire tailscaled[212511]: #011invalid character ‘<’ looking for beginning of value
Jun 17 16:50:42 aspire tailscaled[212511]: #011key hex has the wrong size, got 3075 want 64
Jun 17 16:51:12 aspire kernel: [13689.681227] audit: type=1400 audit(1687013472.091:834): apparmor=“DENIED” operation=“open” class=“file” profile=“snap.chromium.chromium” name=“/proc/pressure/cpu” pid=51990 comm=“ThreadPoolForeg” requested_mask=“r” denied_mask=“r” fsuid=1000 ouid=0
Jun 17 16:51:12 aspire kernel: [13689.681250] audit: type=1400 audit(1687013472.091:835): apparmor=“DENIED” operation=“open” class=“file” profile=“snap.chromium.chromium” name=“/proc/pressure/io” pid=51990 comm=“ThreadPoolForeg” requested_mask=“r” denied_mask=“r” fsuid=1000 ouid=0
Jun 17 16:51:12 aspire kernel: [13689.681264] audit: type=1400 audit(1687013472.095:836): apparmor=“DENIED” operation=“open” class=“file” profile=“snap.chromium.chromium” name=“/proc/pressure/memory” pid=51990 comm=“ThreadPoolForeg” requested_mask=“r” denied_mask=“r” fsuid=1000 ouid=0
Jun 17 16:51:14 aspire tailscaled[212511]: control: LoginInteractive → regen=true
Jun 17 16:51:14 aspire tailscaled[212511]: control: doLogin(regen=true, hasUrl=false)
Jun 17 16:51:14 aspire tailscaled[212511]: Received error: multiple errors:
Jun 17 16:51:14 aspire tailscaled[212511]: #011invalid character ‘<’ looking for beginning of value
Jun 17 16:51:14 aspire tailscaled[212511]: #011key hex has the wrong size, got 3075 want 64
Jun 17 16:51:38 aspire tailscaled[212511]: control: LoginInteractive → regen=true
Jun 17 16:51:38 aspire tailscaled[212511]: control: doLogin(regen=true, hasUrl=false)
Jun 17 16:51:38 aspire tailscaled[212511]: Received error: multiple errors:
Jun 17 16:51:38 aspire tailscaled[212511]: #011invalid character ‘<’ looking for beginning of value
Jun 17 16:51:38 aspire tailscaled[212511]: #011key hex has the wrong size, got 3075 want 64
Jun 17 16:52:20 aspire tailscaled[212511]: control: LoginInteractive → regen=true
Jun 17 16:52:20 aspire tailscaled[212511]: control: doLogin(regen=true, hasUrl=false)
Jun 17 16:52:20 aspire tailscaled[212511]: Received error: multiple errors:
Jun 17 16:52:20 aspire tailscaled[212511]: #011invalid character ‘<’ looking for beginning of value
Jun 17 16:52:20 aspire tailscaled[212511]: #011key hex has the wrong size, got 3075 want 64
Jun 17 16:53:04 aspire tailscaled[212511]: control: LoginInteractive → regen=true
Jun 17 16:53:04 aspire tailscaled[212511]: control: doLogin(regen=true, hasUrl=false)
Jun 17 16:53:04 aspire tailscaled[212511]: Received error: multiple errors:
Jun 17 16:53:04 aspire tailscaled[212511]: #011invalid character ‘<’ looking for beginning of value
Jun 17 16:53:04 aspire tailscaled[212511]: #011key hex has the wrong size, got 3075 want 64
Jun 17 16:53:43 aspire tailscaled[212511]: control: LoginInteractive → regen=true
Jun 17 16:53:43 aspire tailscaled[212511]: control: doLogin(regen=true, hasUrl=false)
Jun 17 16:53:43 aspire tailscaled[212511]: Received error: multiple errors:
Jun 17 16:53:43 aspire tailscaled[212511]: #011invalid character ‘<’ looking for beginning of value
Jun 17 16:53:43 aspire tailscaled[212511]: #011key hex has the wrong size, got 3075 want 64

Yunohost shows now output in /var/log/headscale/headscale.log

When I start headscale /var/log/headscale/headscale.log lists
2023-06-17T16:59:48+02:00 WRN Listening without TLS but ServerURL does not start with http://

server_url: https://hs.<mydomain>.de

I tried base_domain: <mydomain>.de and base_domain: <mydomain>.lan.
Dex-URL is <mydomain>.de/dex

The Tailscale Android-App redirects to login.tailscale.com after some time of trying to register.

Please add a headscale UI!

What’s the current state of Headscale?
Is it working meanwhile?

Hey, Headscale would be awesome for me.

However, I can’t start the daemon, and I get this message when I try to :

$ sudo -u headscale /var/www/headscale/headscale -c /var/www/headscale/config.yaml serve                                1 ↵  ⚡  22:12:10  198.27.80.115 IP 
2024-08-22T22:12:23+02:00 ERR /home/runner/work/headscale/headscale/hscontrol/oidc.go:52 > Could not retrieve OIDC Config: oidc: failed to decode provider discovery object: expected Content-Type = application/json, got "text/html": invalid character '<' looking for beginning of value error="oidc: failed to decode provider discovery object: expected Content-Type = application/json, got \"text/html\": invalid character '<' looking for beginning of value"
2024-08-22T22:12:23+02:00 FTL /home/runner/work/headscale/headscale/cmd/headscale/cli/server.go:21 > Error initializing error="oidc: failed to decode provider discovery object: expected Content-Type = application/json, got \"text/html\": invalid character '<' looking for beginning of value"

Hi Parleur!

I had this issue as well. In the Headscale install page, it asks for a sub-domain for Dex. Turns out that you need to install Dex after installing Headscale, in that location.

You will need 3 pieces of information: client_id, client_secret and callback URI.

The client_id and client_secret are in the oidc section of the Headscale config file.

As per the Headscale docs, the callback URI is the sub-domain you’ve installed it into with oidc/callback at the end.

After doing this and nothing more, I am now able to start the service.

Now I just need to get clients connecting. :smiley:

I hope this solves your issue as well!

I have published a fix last night, the Dex installation command included in the install script for Headscale was not working anymore.

3 Likes

So, just to ask a question… If I want to make Yunohost services available over TailScale, using only my own server (eg no Google/Microsoft/Apple login), would I only need to install this app, or would I need to install Dex, too?

PS The “YunoHost documentation for this app” link seems to go to a 404 page…

This app uses DEX as the OIDC auth provider. So the installation script will try and install DEX (which is broken btw, as of now).

In vanilla headscale you do not need DEX.

this means the headscale that installs through gui does not work ?
and what about headplane, how do i get the token ?

i mean, i have installed tailscale, it shows a page that says “its installed here” ok, how do i configure it now ?
trying so answer this question i found that headplane is available and installed it but nginx say it is not working…

Hi, I have the same headplane issue. I installed it, it works in appearance but I get 404 Page not found error. So I opened this issue on its yunohost repo since 2 weeks. But it seems like none of the developers have stopped by the repo since one month.