Skip to content

Commit

Permalink
Post Tailscale VPN articles
Browse files Browse the repository at this point in the history
  • Loading branch information
radarsymphony committed Nov 1, 2023
2 parents 86c485e + d3a5854 commit d0e8013
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 0 deletions.
153 changes: 153 additions & 0 deletions content/posts/headscale-for-tailscale-vpn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
---
title: Heads or Tailscale VPN
date: 2023-10-31T15:01:39-07:00
draft: false
type: post
showTableOfContents: true
tags:
- vpn
- tailscale
- headscale
- opensource
- server
- homelab
- network
---
# Overview
This guide outlines how to set up [Headscale](https://headscale.net/) running as Docker container behind a reverse proxy (Traefik). It uses a free ubuntu VPS from the Oracle Cloud Free Tier, but any linux-based host with public IP and about ~1GB of memory should work for small Home Lab setups.

Headscale is an opensource reverse-engineered implementation of the closed source Tailscale coordination server. There are many advantages to using the original Tailscale coordination server, such as a feature admin panel and multiple tailnets. However, I am on a quest to explore opensource and privacy-focused software, I've decided to set up Headscale as my Tailscale coordination server.

Setting up Headscale behind a reverse-proxy is not something that the maintainers support or use themselves, but it _is_ a feature that is often [requested by community members](https://github.com/juanfont/headscale/issues/527). I wanted to see if I could identify a way to configure Headscale behind Traefik as a reverse proxy. The following is my working prototype.
#### Prerequisites
- Linux VPS with Public IP and ~1GB of memory
- Traefik running in docker container ([Review this guide](/posts/traefik-reverse-proxy))
- Public domain
- [Must read this Headscale documentation](https://github.com/juanfont/headscale/blob/main/docs/reverse-proxy.md)
# VPS Setup
1. Create a free VPS with Oracle Cloud.
2. Point a hostname to the public IP of your VPS (e.g. `headscale.example.com`).
3. Update packages and install docker and docker-compose.
4. Set up a container running Traefik as described in [this guide](/posts/traefik-reverse-proxy).
# Headscale Setup
1. Login to the VPS with SSH.
2. Create a directory for Headscale: `mkdir /srv/apps/headscale`
3. Change to that directory: `cd /srv/apps/headscale`
4. Create a compose.yml file and paste the following into, updating values as required:

```yaml
services:
headscale:
image: headscale/headscale:latest
container_name: headscale
hostname: headscale
labels:
- traefik.enable=true
- traefik.http.routers.headscale-example-com.tls=true
- traefik.http.routers.headscale-example-com.rule=Host(`headscale.example.com`)
- traefik.http.routers.headscale-example-com.service=headscale-example-com
- traefik.http.services.headscale-example-com.loadbalancer.server.port=8080
ports:
- 3478:3478/udp # did not need to open in Oracle VPC firewall
volumes:
- ./data/headscale:/var/lib/headscale
- ./data/config:/etc/headscale
networks:
- proxy
restart: unless-stopped
command: "headscale serve"

networks:
proxy:
external: true
```
5. Create the config directory: `mkdir -p data/config`
6. Create the sqlite db: `touch data/config/db.sqlite`
7. Create and edit the config file: `vi data/config/config.yaml` (**.yaml** not .yml seems to matter)
8. Paste in the configuration below, updating values as required:

```yaml
---
server_url: https://headscale.example.com
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 0.0.0.0:9090
grpc_listen_addr: 0.0.0.0:50443
grpc_allow_insecure: false
private_key_path: /var/lib/headscale/private.key
noise:
private_key_path: /var/lib/headscale/noise_private.key
ip_prefixes:
- fd7a:115c:a1e0::/48
- 100.64.0.0/10
derp:
server:
enabled: false
region_id: 999
region_code: "headscale"
region_name: "Headscale Embedded DERP"
stun_listen_addr: "0.0.0.0:3478"
urls:
- https://controlplane.tailscale.com/derpmap/default
paths: []
auto_update_enabled: true
update_frequency: 24h
disable_check_updates: false
ephemeral_node_inactivity_timeout: 30m
node_update_check_interval: 10s
db_type: sqlite3
db_path: /etc/headscale/db.sqlite
acme_url: https://acme-v02.api.letsencrypt.org/directory
tls_letsencrypt_challenge_type: HTTP-01
tls_letsencrypt_listen: ":http"
tls_cert_path: ""
tls_key_path: ""
log:
format: text
level: info
acl_policy_path: ""
dns_config:
override_local_dns: true
nameservers:
- 1.1.1.1
- 1.0.0.1
domains: []
magic_dns: true
base_domain: example.com
unix_socket: /var/run/headscale/headscale.sock
unix_socket_permission: "0770"
logtail:
enabled: false
randomize_client_port: false
```

**Note:** [The full Headscale configuration](https://github.com/juanfont/headscale/blob/main/config-example.yaml) with comments and default values can be found on their Github. This original will likely be useful when debugging.

# Connecting Tailscale Nodes
This process is fairly straight forward and based on the [Headscale documentation](https://headscale.net/running-headscale-linux/#using-headscale).
The only caveat is that you need to prefix the `tailscale` commands with `docker exec [tailscale_container]...`
# Toubleshooting
- Headscale makes a [larger debug-focus version of the headscale image](https://headscale.net/running-headscale-container/#debugging-headscale-running-in-docker). This is useful to include more tools within the container to check connections and troubleshoot.

It took me a little bit of testing to arrive at the combination of URL and listening address. In the end, I found that the following worked well. Initially I was trying to use port 443 (which some other tutorials showed) and received errors like `Client sent an HTTP request to an HTTPS server.`
```yaml
server_url: https://headscale.example.com
listen_addr: 0.0.0.0:8080
```

It was important to leave the `tls_cert_path` and `tls_key_path` empty, as discussed on the [Headscale Github](https://github.com/juanfont/headscale/blob/main/docs/tls.md#bring-your-own-certificate).
```yaml
acme_url: https://acme-v02.api.letsencrypt.org/directory
tls_letsencrypt_challenge_type: HTTP-01
tls_letsencrypt_listen: ":http"
tls_cert_path: ""
tls_key_path: ""
```

If you have suggestions or would like another pair of eyes to toubleshoot, please reach out by opening an issue in the repo that tracks this website. You can find it here:

[https://github.com/radarsymphony/radarsymphony.github.io/issues](https://github.com/radarsymphony/radarsymphony.github.io/issues)
# Resources
- [Headscale discussion on reverse-proxies](https://github.com/juanfont/headscale/blob/main/docs/reverse-proxy.md)
- [Headscale documentation on running as container](https://headscale.net/running-headscale-container/)
- [Another blog outlining a similar process](https://techoverflow.net/2022/12/28/headscale-docker-compose-config-for-traefik-https-reverse-proxy/)
1 change: 1 addition & 0 deletions content/posts/local-dns-with-bind9.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ tags:
- bind9
- resolver
- docker
- network
---
# Overview
This short article outlines the steps to set up a local DNS server using [BIND9](https://bind9.net/) and Docker on a Raspberry Pi. This approach enables you to manage your own zone(s) for local services running in a Home Lab. It may also help cache DNS queries to reduce lookup time for frequently requested resources. I've only tested this approach in protected local networks.
Expand Down
83 changes: 83 additions & 0 deletions content/posts/tailscale-mesh-vpn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
title: Tailscale Mesh VPN
date: 2023-10-31T13:47:56-07:00
draft: false
type: post
showTableOfContents: true
tags:
- VPN
- homelab
- tailscale
- network
- docker
---
# Overview
[Tailscale](https://tailscale.com/) is an easy to configure mesh VPN. It uses [NAT traversal](https://tailscale.com/blog/how-nat-traversal-works/) to connect peers to each other. This article will outline the steps to set up Tailscale running in Docker as if it were running on the docker host directly (without SSH over the VPN - yet). This approach enables managing the Tailscale connection as you would any other docker service and creates a portable and deploy-able compose.yml to run on other systems.
# Tailscale Account
Tailscale makes use of a control panel to orchestrate and connect nodes. In order to connect your devices together, you need to create an account with Tailscale.

> In [this article](/posts/headscale-for-tailscale-vpn), I modify the VPN outlined here to move away from the closed source Tailscale control server to the opensource Headscale control server.
1. Create an account with Tailscale [login.tailscale.com](https://login.tailscale.com/)
2. Go to **Settings** > **Personal Settings** > **Keys**
3. Click **Generate auth key**, turn on **Reusable**
4. Click **Generate key** and save the value.
# Docker Compose
1. Create a directory to host this service: `mkdir /srv/apps/tailscale`
2. Change into that directory: `cd /srv/apps/tailscale`
3. Create a compose.yml file: `touch compose.yml`
4. Open the file up with an editor and paste in the following:

```yaml
services:
tailscale:
image: tailscale/tailscale
container_name: tailscale
network_mode: "host"
environment:
- TZ=[YOUR_TIMEZONE]
- TS_HOSTNAME=${HOSTNAME}-tailscale
- TS_AUTHKEY=[TS_AUTHKEY]
- TS_STATE_DIR=/tailscale/state
- TS_SOCKET=/var/run/tailscale/tailscaled.sock
- TS_TAILSCALED_EXTRA_ARGS=--tun=tailscale0 # important
- TS_DEBUG_FIREWALL_MODE=nftables
- TS_NO_LOGS_NO_SUPPORT=true
volumes:
- ./data/tailscale/state:/tailscale/state
- /dev/net/tun:/dev/net/tun # important
cap_add:
- net_admin
- sys_module
restart: unless-stopped
```
5. Update the TS_AUTHKEY that you generated in the account.
# Start Tailscale
1. While logged in to the Tailscale control panel, go to [the machines tab](https://login.tailscale.com/admin/machines).
2. In your terminal at the Tailscale directory, run: `docker-compose up -d`
3. Refresh the machines tab and you should see a new node appear.
# Testing
A quick way to test if the VPN is working is to install the [Tailscale app on a mobile device](https://tailscale.com/downloads). You should be able to see both devices registered in your machine's tab in Tailscale.
# Troubleshooting
Start simple and progress in small steps. When I set this up, I started with installing the Tailscale binary on two servers (no Docker) exactly like the Tailscale documentation described. This approach allowed me to just see if the two hosts would be able to connect as intended before adding another layer of complexity. I then slowly transitioned one server to use Docker, then the other.

Oracle Cloud Free Tier allows the provision of a small VPS within minutes that can be useful for having another remote (plain/fresh) node to set up Tailscale on and test iterations.

I frequently ran `docker exec tailscale tailscale [status|ping <tailscale-hostname>]` to see if the current node was registering other nodes and to gain insight into things like whether it was using IPv4 or IPv6.

I initially had issues with [iptables vs nftables](https://github.com/tailscale/tailscale/issues/5621) and found that for my devices I had to set `TS_DEBUG_FIREWALL_MODE=nftables`. If you encounter issues, you can try removing this value.

To run Tailscale in Docker as if it were running on the host posed some challenges. The nodes were registering but not fully connecting to each other until I added `TS_TAILSCALED_EXTRA_ARGS=--tun=tailscale0`. By default, the [Tailscale Docker image enables "userspace-networking"](https://tailscale.com/kb/1282/docker/?q=docker#ts_userspace) which doesn't seem to work in this setup.

Depending on your firewall, you may find pointers in the [Tailscale firewall documentation](https://tailscale.com/kb/1181/firewalls/).

If you have suggestions or would like another pair of eyes to toubleshoot, please reach out by opening an issue in the repo that tracks this website. You can find it here:

[https://github.com/radarsymphony/radarsymphony.github.io/issues](https://github.com/radarsymphony/radarsymphony.github.io/issues)
# Resources
- [Tailscale Terminology and Concepts reference](https://tailscale.com/kb/1155/terminology-and-concepts/)
- https://www.wundertech.net/how-to-set-up-tailscale-on-docker/
- https://docs.ibracorp.io/tailscale/tailscale/docker-compose
- [Similar approach but Tailscale within each docker container](https://tailscale.dev/blog/docker-mod-tailscale)
- [Setting up Headscale to run in Docker behind Reverse Proxy](/posts/headscale-for-tailscale-vpn)
1 change: 1 addition & 0 deletions content/posts/traefik-reverse-proxy.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ tags:
- reverse-proxy
- homelab
- https
- docker
---
# Overview
The following guide outlines the steps to run [Traefik](https://traefik.io/traefik/) with docker as a reverse proxy for your host. This setup enables you to resolve hostnames to particular containers running on the host. With a public domain, you can use [Traefik to request SSL certificates to enable https](https://doc.traefik.io/traefik/https/acme/) for each site.
Expand Down

0 comments on commit d0e8013

Please sign in to comment.