[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

3 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?