Skip to content

Instantly share code, notes, and snippets.

@Und3rf10w
Last active April 30, 2024 10:43
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Und3rf10w/1cd618f0656191b7db30fb29aaa35678 to your computer and use it in GitHub Desktop.
Save Und3rf10w/1cd618f0656191b7db30fb29aaa35678 to your computer and use it in GitHub Desktop.
How to set up headscale with SWAG in docker-compose

Overview

This document descrives the process of setting up headscale with swag, using letsencrypt certs.

This assumes you have a subdomain (e.g. wg.example.com) pointing to your SWAG instance and want to use it as a reverse proxy.

Swag .env

Add the subdomain for your desired headscale domain to your .env file.

For example: SWAG_EXTRA_DOMAINS=wg.example.com

Headscale Setup

We have to create the headscale configuration files and directories.

mkdir -p ./headscale/config
cd ./headscale
touch ./config/db.sqlite

You can copy the example configuration from the headscale repository.

curl https://raw.githubusercontent.com/juanfont/headscale/main/config-example.yaml -o ./config/config.yaml

Headscale Config

Set the following on values inside your config/config.yaml file:

This is assuming you want your clients to hit your headscale instance at https://wg.example.com:443

server_url: https://wg.example.com:443
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
tls_cert_path: ""
tls_key_path: ""

Comment out the following stanzas inside config/config.yaml file:

# tls_letsencrypt_listen: ":http"
# tls_letsencrypt_cache_dir: ./cache
# tls_letsencrypt_hostname: ""
# acme_email: ""
# acme_url: https://acme-v02.api.letsencrypt.org/directory

You can configure the rest of the options as you wish.

Headscale proxy configuration

Create the headscale proxy-conf:

touch config/nginx/proxy-confs/headscale.subdomain.conf

Assuming your container name is going to be wg, put the following in that file:

map $http_upgrade $connection_upgrade {
    default      keep-alive;
    'websocket'  upgrade;
    ''           close;
}

server {
  listen 443      ssl http2;
  listen [::]:443 ssl http2;

  server_name wg.*;

  include /config/nginx/ssl.conf;


  location / {
    include /config/nginx/proxy.conf;
    include /config/nginx/resolver.conf;
    set $upstream_app wg;
    set $upstream_port 8080;
    set $upstream_proto http;
    proxy_pass $upstream_proto://$upstream_app:$upstream_port;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_buffering off;
    add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
  }
}

Docker-compose.yml

Finally, we'll add the headscale service to our docker-compose.yml file where SWAG runs:

  wg:
    image: headscale/headscale:latest
    container_name: wg
    hostname: wg
    restart: always
    ports:
      - "8080:8080"
      - "9090:9090"
    volumes:
      - ./headscale/config:/etc/headscale/
    command: headscale serve

You only have to add the ports: stanza if you want to expose that on the server running the containers.

Finally, run docker compose up -d --force-recreate and your headscale container will be ready to run.

You can run headscale commands with docker exec wg headscale $HEADSCALE_ARGS

Setting up an exit node

This seciton describes setting up an exit node to route all traffic through on a Linux machine. The machine used for this demo is an Ubuntu 20.04 server, but commands should be general enough to apply to most setups.

Install tailscale

Install tailscale on the linux host by following the official documentation. At the time of writing, this simply requires:

curl -fsSL https://tailscale.com/install.sh | sh

Now that tailscale is installed, we need to set up ip forwarding:

echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p /etc/sysctl.conf

Running tailscale exit node

Finally, we can start the node, advertise it as an exit node, and advertise routes. You can set whatever subnets you want, but we'll just use all traffic for the sake of this document. The routes we will publish are 0.0.0.0/0 and ::0.

Replace $LOGIN_SERVER with the hostname that you configured for your headscale host, e.g. wg.example.com.

sudo tailscale up --advertise-exit-node --login-server https://$LOGIN_SERVER --advertise-routes=0.0.0.0/0,::/0 --force-reauth

Note on mDNS

Now the node we just set up can be used as an exit node, and all traffic will be routed through it. Your client can use it to access local resources on the network. However this setup does route mDNS traffic to wireguard nodes. In order to do that, you have to enable multicast on the tailscale interface. By default, the tailscale interface will be tailscale0, change this as appropriate for your environment.

ip link set multicast on dev tailscale0

Running this command will forward mDNS traffic to your wireguard hosts, allowing you to use applications like Chromecast, IGMP, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment