A great intranet penetration solution——Self-hold Tailscale: HeadScale

Why need intranet penetration?

Days before, I just use IPSec VPN as my intranet solution. But it has a limit: I can only connect to my dormitory when I am connected to our campus network. If I am using mobile data, I can not connect it as the firewall rule of our school. I do not think this is a big deal since most of time I just stay in school.

But when vocation comes, I need to shutdown all my servers for the reason that I can not control my server if some incidents occuur. Suppose I have a real intranet penetration resolution, there is no need to power down my server owing to the ability to remote monitoring and control of my server. So I can run my programs 24/7.

Options

I have considering several decisions for NAT4 which is my internet environment. Finally, zerotier and tailscale are left for me to choose. At last, I choose tailscale due to its protocol is open source wireguard while zerotile is using private protocol.

Why I am using self-hold Tailscale——Headscale?

The free version of Tailscale has a series limit. And self-hold tailscale is more secure and customizable.

How to deploy

Headscale server installation

1
2
3
4
5
wget https://github.com/juanfont/headscale/releases/download/v0.22.3/headscale_0.22.3_linux_amd64.deb
mv headscale_0.22.3_linux_amd64.deb headscale.deb
sudo dpkg --install headscale.deb
sudo systemctl enable headscale
vim /etc/headscale/config.yaml
1
2
3
4
5
server_url: https://your_domain:443
listen_addr: 127.0.0.1:8080
metrics_listen_addr: 127.0.0.1:9090
grpc_listen_addr: 127.0.0.1:50443
randomize_client_port: true

Headerscale web UI installation

1
2
3
4
5
6
wget https://github.com/gurucomputing/headscale-ui/releases/download/2023.01.30-beta-1/headscale-ui.zip
unzip headscale-ui.zip
mv web /var/www/
cd ~ #In my situation, ~ is /root
sudo certbot certonly --nginx -d your_domain
vim /etc/nginx/sites-available/00-default-ssl.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
ssl_certificate /etc/letsencrypt/live/your_domain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your_domain/privkey.pem;
server_name your_domain;
location /web {
alias /var/www/web;
index index.html;
}
location / {
proxy_redirect off;
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
}
}
1
service nginx restart

Start Headscale service

1
2
headscale users create your_username
headscale apikeys create

Go to https://your_domain/web and switch to settings, input your Headscale url(do not need port, just like https://your_domain) and Headscale API key. Test if it is okay.

Install Tailscale client

For Android

Just see:

https://github.com/juanfont/headscale/blob/main/docs/android-client.md

For Mac OS

Just see:

https://your_domain/apple

For Linux (Ubuntu 22.04)

Just see:

https://tailscale.com/download/linux/ubuntu-2204

Self-hold derper

When a p2p connection can not be established, Tailscale client will use relay server called derper. Since all official derper servers are aboard, it is essential for us to get a better experience.

In my circumstance, I have a oversea VPS serving as Headscale server, and a domestic VPS serving as Derper.

Notice: Due to my VPS on Ali Cloud can not do ICP filing, I need to do some extra works to enable it. If you have finished ICP filing, just follow the official instruction.

Before enter your command, you may need to config firewall to pass all ICMP for both IPV4 and IPV6 and open needed ports mentioned blow.

1
2
3
4
5
apt update
apt upgrade
wget https://go.dev/dl/go1.21.1.linux-amd64.tar.gz
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.21.1.linux-amd64.tar.gz
vim .profile
1
export PATH=$PATH:/usr/local/go/bin
1
2
3
4
5
6
7
source .profile
go version
vim .profile #add export GOPROXY=https://proxy.golang.com.cn,direct to use proxy for go
source .profile
go install tailscale.com/cmd/derper@main
cd go/pkg/mod/tailscale.com@v1.1.1-0.20231002192404-5efd5e093ee0/cmd/derper/
vim cert.go #Disable the domain name validation function.
1
2
3
//if hi.ServerName != m.hostname {
// return nil, fmt.Errorf("cert mismatch with hostname: %q", hi.ServerName)
//}
1
2
go build -o ~ #rebuild a new version derper, the output file will be in the ~
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout /root/derp.myself.com.key -out /root/derp.myself.com.crt -subj "/CN=derp.myself.com" -addext "subjectAltName=DNS:derp.myself.com" #self signed certificate

Before you start your derper server, in order to avoid unauthorized access to your DERP server, we need to start Tailscale service in your derp server.

After installing Tailscale.

1
tailscale up --login-server=https://your_domain #Then follow the instruction in termianl
1
screen #create a virtual terminal for derper
1
2
cd ~
./derper --hostname derp.myself.com --a :33445 --certmode manual -certdir /root --verify-clients

Press Alt+A, then press D to detach this virtual terminal.

1
2
3
apt install nginx
cd /var/www/html/
vim derp.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"Regions": {
"901": {
"RegionID": 901,
"RegionCode": "BJ",
"RegionName": "Ali Cloud",
"Nodes": [
{
"Name": "901a",
"RegionID": 901,
"DERPPort": 33445,
"HostName": "your_derp_ipv4_address",
"IPv4": "your_derp_ipv4_address",
"IPv6": "your_derp_ipv6_address",
"InsecureForTests": true
}
]
}
}
}

Notice: You need to open port 3478 for UDP, port 33445 for TCP in firewall

Then go to your Headscale VPS.

1
vim /etc/headscale/config.yaml
1
2
3
4
5
derp:
server:
url:
#- https://controlplane.tailscale.com/derpmap/default
- http://your_derp_server_ipv4_address/derp.json

Using local paths with yaml file is ineffecitive in this situation for unfiled VPS. If you are using unfiled VPS as derp server, you can only use json to configure self-hold derp server.

1
reboot # I do not know why it would spend long time if I use command: service headscale restart. You can also use service headscale if you'd like to wait.

How to use local LAN ip directly

It would be very inconvenient just to use ip address signed by Headscale. It’s not just because IP addresses are hard to remember, but also because you need to deploy the Tailscale service on every device in the local network. Want ot use local LAN ip directly? Just go ahead.

If we advertise our routes on DNS, we can use our local LAN ip directly.

Installation Tailscale in OpenWrt

1
2
3
4
5
6
7
8
9
10
wget https://github.com/adyanth/openwrt-tailscale-enabler/releases/tag/v1.36.1-fb2f6c
f-autoupdate
tar x -zvC / -f openwrt-tailscale-enabler-<tag>.tgz
# Make sure your OpenWrt has installed these packages: libustream-openssl ca-bundle kmod-tun.
/etc/init.d/tailscale start
#allow 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.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conf
tailscale up --login-server=https://your_domain --advertise-routes=192.168.5.0/24

Then go to https://your_domain/web, switch to Device View, select your openwrt and click Device Routes’ button from pending to avtive.

How to run a exit node.

We can route traffic to exit from a specific exit node to utilize that node’s network environment.

I am using a Ubuntu Server in my campus as an exit node so that I can use my campus network and OpenClash in my domitory.

1
2
3
4
5
6
# After install Tailscale client
#allow 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.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conf
tailscale up --login-server=https://your_domain --advertise-exit-node

Then go to https://your_domain/web, switch to Device View, select your exit node server and click Device Routes’ button from pending to avtive. After this, you can see and use your exit node in other Tailscale client.

Reference

Announcement

Words fail to express my appreciation to 姿势小王子, his post made me realise that if my server is not filed, I must use json to config my derp server, otherwise will appears TLS handshake error form ……:write: connection reset by peer in derp server and connection to derp server would fail.