Headscale是自建Tailscale控制伺服器,考慮到Tailscale樹大招風,可能在某些區域被限制,所以改用全部自己搭建。
建立容器要用到的設定檔:
首先在docker的電腦建立文件
# 建立資料夾(路徑可自行更改,下方的 YAML 要對應)
mkdir -p /home/user/headscale/config
mkdir -p /home/user/headscale/data
# 建立一個空的 config.yaml (稍後需填入內容)
touch /home/user/headscale/config/config.yaml
其中的user要更換爲實際的使用者,比如電腦的使用者是icka,則指令相應變為
# 建立存放設定檔與資料的資料夾
mkdir -p /home/icka/headscale/config
mkdir -p /home/icka/headscale/data
也可以運行下面的確認權限,但也可以不加看需要
sudo chown -R icka:icka /home/icka/headscale
sudo chmod -R 777 /home/icka/headscale/data
# 建立空的 config.yaml,避免容器啟動時報錯
touch /home/icka/headscale/config/config.yaml
然後真實的修改yaml檔案,是headscale的設定檔
nano /home/icka/headscale/config/config.yaml
內容如下
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# --- https替換爲實際要用的控制域名,其他電腦註冊加入都會訪問這個域名 --- server_url: https://headscale.example.com listen_addr: 0.0.0.0:8080 metrics_listen_addr: 0.0.0.0:9090 database: type: sqlite sqlite: path: /var/lib/headscale/db.sqlite prefixes: v4: 100.64.0.0/10 dns: magic_dns: true # --- example.com更換爲實際的域名 --- base_domain: tailscale.example.com override_local_dns: true nameservers: global: - 8.8.8.8 - 1.1.1.1 noise: private_key_path: /var/lib/headscale/noise_private.key # --- 這裡插入 ACL 設定,我的環境是用database方式,如果不用ACL則下面3行可以不用 --- policy: # 模式可以選 "file" 或 "database" mode: database derp: server: enabled: true region_id: 999 region_code: "headscale" region_name: "Headscale Embedded DERP" stun_listen_addr: "0.0.0.0:3478" private_key_path: /var/lib/headscale/derp_server_private.key log_level: info |
然後管理工具headplane的設定檔
nano /home/icka/headscale/headplane-config.yaml
內容如下,其中的自訂一組KEY請修改可以自行修改
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
headscale: url: "http://headscale:8080" # 這裡是讓 UI 管理 Headscale 的路徑,必須與 Docker 掛載路徑一致 config_path: "/etc/headscale/config.yaml" server: # 隨便填寫一段長字串,用於加密網頁 Session cookie_secret: "icka_secret_key_32_characters_!!" # 這是你進入網頁管理介面要輸入的密碼 root_api_key: "自訂一組KEY請修改" disable_auth: false integration: # 開啟 Docker 整合功能,才能在網頁改 DNS 設定 docker: enabled: true |
Portainer建立headscale和headplane容器:
然後到Portainer,點擊Stacks—Add stack,Name填寫headscale,Web editor填寫下面的內容,其中的headscale域名好和設定檔的一致,headplane要另外設一個,volumes等也要修改爲自己環境裏面對應的路徑,然後我的環境因爲使用了nginx-proxy和nginx-proxy-acme,所以需要VIRTUAL_HOST和LETSENCRYPT_HOST參數,才需要添加,如果你的環境里不是用的這個來做反代和證書申請,則相應的修改或者去掉,我的網絡是使用現有另外建立的,名字是chatgpt,實際使用可以根據實際的名字修改。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
version: '3.9' services: # --- Headscale 主程式 --- headscale: image: ghcr.io/juanfont/headscale:latest container_name: headscale restart: unless-stopped volumes: - /home/icka/headscale/config:/etc/headscale - /home/icka/headscale/data:/var/lib/headscale ports: - "8085:8080" command: serve labels: # 【官網要求】必須有這行標籤,Headplane 才能識別它,下面的headscale.example.com要修改爲實際的headscale域名 - "me.tale.headplane.target=headscale" environment: - VIRTUAL_HOST=headscale.example.com - VIRTUAL_PORT=8080 - LETSENCRYPT_HOST=headscale.example.com networks: - chatgpt # --- Headplane 管理介面 --- headplane: image: ghcr.io/tale/headplane:latest container_name: headplane restart: unless-stopped volumes: # 1. 掛載剛才建立的 Headplane 設定檔 - /home/icka/headscale/headplane-config.yaml:/etc/headplane/config.yaml # 2. 持久化資料夾 - /home/icka/headscale/headplane-data:/var/lib/headplane # 3. 共享 Headscale 設定檔 (讓 UI 可以修改網路設定) - /home/icka/headscale/config:/etc/headscale # 4. 掛載 Docker Socket (啟用進階管理功能) - /var/run/docker.sock:/var/run/docker.sock:ro environment: # Nginx 反代設定,下面的域名要改成Headplane要用的域名,這個和前面的headscale是不同的 - VIRTUAL_HOST=headplane.example.com - VIRTUAL_PORT=3000 - LETSENCRYPT_HOST=headplane.example.com networks: - chatgpt networks: chatgpt: external: true |
然後點擊update the stack,正常情況下就會產生2個新的容器。
登陸headplane管理平台:
在linux中建立key
sudo docker exec -it headscale headscale apikeys create
# 產生一個 99 年後才過期的 Key (等同於永久)
sudo docker exec -it headscale headscale apikeys create --expiration 99y
這樣會產生比如 hskey-api-6NzayppbQ6k4-P12X70aagewrgerghethryhtyjyujyujk 這樣的key,記錄下來,訪問https://headplane.example.com/admin/login,填寫上面的KEY就可以進行管理。
客戶端安裝加入:
客戶端下載:https://tailscale.com/download
安裝好後不要去連線tailscale網站登陸,註冊到自己的服務器都需要手動操作。
Windows:
在cmd或者powershell中運行(不需要管理員),下面的headscale.example.com更換實際的headscale域名,我的環境因爲有反代到https的443port,如果沒有反代則可能是Http的8080 port
tailscale login --login-server https://headscale.example.com
這樣就會返回一個url裏面帶着一組Key

回到headplane的管理界面,先點擊Users,點擊Add a new user,比如user1,然後點擊Machines—Add Device— Register Machine Key,填寫客戶端產生的key,owner下拉選擇屬於哪個用戶,建議按照實際用戶選擇,因爲不同使用者的訪問權之後可以在ACL中設定。
Linux:
tailscale up --login-server https://headscale.example.com
iOS / Android:
IOS是在非大陸版商店免費下載,安卓也可以在play商店下載,或者訪問https://github.com/tailscale/tailscale-android/releases 下載apk
打開 Tailscale App,先不要登入。進入設定(Settings),找到 "Server" 或連點左上角圖標開啟隱藏選單。
將原本的官方網址改為 實際的URL比如https://headscale.example.com。點擊登入,系統會跳轉到授權頁面。
設定跳板節點exit node:
設定在 Windows 右下角找到 Tailscale 圖標點擊 右鍵。選擇Exit nodes,然後勾選Run exit node。

這樣設定後這台電腦可以被其他電腦來當作跳板機來訪問外網了,但實際上是否看的到及可以訪問什麼內容可以用過ACL設定,如果沒有設ACL預設的不加任何限制,想要做部分限定只能通過跳板機的本地防火牆來限制,畢竟預設訪問過來的IP是100.64.0.0/10網段的。勾選完run exit node後,就只是這個客戶端願意當跳板機了,還需要經過服務器的允許才可以,然後去https://headplane.example.com/admin/login網站管理,打開此電腦的Edit route settings啟用Use as exit node

另外能讓windows重啓沒有登入的情況下,taiscale也能自動運行,可以選擇 Preferences (偏好設定),勾選 Run Unattended (在背景運行)。系統會跳出提示,詢問是否要在無人登入時保持連線,點擊 「是」。

保持電腦開啟的小工具,未驗證有效性
https://github.com/CHerSun/NoSleep
https://www.zhornsoftware.co.uk/caffeine/
https://github.com/sw3103/movemouse
建立socks5代理(可選):
此時其他電腦如果使用上述電腦來訪問外網是全局的,但有的時候中是希望部分服務通過跳板機訪問,這也是可以做到的,畢竟這些電腦目前是在同一個內網當中。我們可以用一些工具在這個電腦上架設socks5代理
比如使用gost,這個軟體會被windows認爲是有害的軟體,所以想要運行需要添加排除。
https://github.com/go-gost/gost/releases
使用 NSSM 變成服務:
下載 NSSM https://nssm.cc/download
以管理員執行 PowerShell,進入 NSSM 的 win64 資料夾。
輸入:.\nssm.exe install GOST_Proxy。
在彈出的視窗中路徑選擇gost的實際路徑,比如Path: C:\gost\gost.exe
Arguments的地方輸入: -L socks5://:10806
點擊 Install service。這樣就可以在本機開啓一個10806的socks5代理,其他的電腦在沒有全局連線跳板機訪問的時候,就只需要開着taiscale,就可以通過比如瀏覽器插件來設定socks5代理訪問,另外在宿主的主機上要開放10806port的訪問。
如果報錯可以用.\gost.exe -L socks5://:10806 看報錯內容排查,比如port被佔用等等,這個port號可以自己更改,此處是爲了防止其他軟體佔用1080才更改的10806
搭建的服務Portainer的主機要開的port:
TCP 80,443因做了反代,所以訪問和驗證實際變爲80和443
UDP 3478 (STUN 服務)(這是用來「打洞」的。它像是一面鏡子,當你連上網路時,會透過這個 UDP 埠問伺服器:「我的外網 IP 和 NAT 類型是什麼?」,獲取資訊後雙方才能建立 P2P 直連。)
如果跳板伺服器是windows,想中允許跳板訪問外網和訪問10086可以嘗試下面的指令,未驗證有效性
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# 1. 定義變數 $TailscaleRange = "100.64.0.0/10" $ProxyPort = 10806 # 2. 清理舊規則(確保重複執行不會出錯) Remove-NetFirewallRule -DisplayName "Headscale-*" -ErrorAction SilentlyContinue # 3. 【阻擋】445 (SMB) 和 3389 (RDP) New-NetFirewallRule -DisplayName "Headscale-Block-Private" ` -Direction Inbound ` -Action Block ` -Protocol TCP ` -LocalPort 445, 3389 ` -RemoteAddress $TailscaleRange ` -Description "禁止 VPN 內網存取本機私有服務" # 4. 【允許】10806 (SOCKS5 代理) New-NetFirewallRule -DisplayName "Headscale-Allow-SOCKS5" ` -Direction Inbound ` -Action Allow ` -Protocol TCP ` -LocalPort $ProxyPort ` -RemoteAddress $TailscaleRange ` -Description "允許 VPN 內網使用 SOCKS5 代理" # 5. 【新增:允許 Ping】ICMPv4 New-NetFirewallRule -DisplayName "Headscale-Allow-Ping-v4" ` -Direction Inbound ` -Action Allow ` -Protocol ICMPv4 ` -IcmpType 8 ` -RemoteAddress $TailscaleRange ` -Description "允許 VPN 內網進行 Ping (IPv4) 測試" # 6. 【新增:允許 Ping】ICMPv6 New-NetFirewallRule -DisplayName "Headscale-Allow-Ping-v6" ` -Direction Inbound ` -Action Allow ` -Protocol ICMPv6 ` -IcmpType 128 ` -RemoteAddress $TailscaleRange ` -Description "允許 VPN 內網進行 Ping (IPv6) 測試" Write-Host "--- 設定更新完成 ---" -ForegroundColor Green Write-Host "✅ 已封鎖 445, 3389 (RDP/SMB)" Write-Host "✅ 已開放 $ProxyPort (SOCKS5)" Write-Host "✅ 已開放 Ping (ICMP) 測試" |
ACL部分:
如果沒有設定ACL設定檔,則ACL的部分是無法查看的,解決方式是使用database的方式,如文章開頭的設定,這樣訪問管理界面的時候就可以設定使用者的權限。

簡單的範例,未驗證
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
{ // 1. 定義使用者群組 (每個名稱後面一定要加 @) "groups": { "group:admin": ["icka@"], "group:u1": ["user1@"], "group:u2": ["user2@"], }, // 2. 定義標籤擁有者 "tagOwners": { "tag:jp1": ["group:admin"], "tag:hk1": ["group:admin"], "tag:sg1": ["group:admin"], "tag:kr1": ["group:admin"], "tag:au1": ["group:admin"], }, "acls": [ // --- 【管理員最強規則】 --- // 讓 icka 看到並存取「所有」設備 { "action": "accept", "src": ["group:admin"], "dst": ["*:*"] }, // --- 【使用者 user1】(僅限標籤jp1) --- { "action": "accept", "src": ["group:u1"], "dst": [ "tag:jp1:10806", "0.0.0.0/0:*", "::/0:*" // 這是 Exit Node 的標準寫法,代表允許連往網際網路 ], }, // --- 【使用者 user2】(僅限標籤hk1) --- { "action": "accept", "src": ["group:u2"], "dst": [ "tag:hk1:10806", "0.0.0.0/0:*", "::/0:*" ], }, ], } |
而所有的裝置在加入的時候就可以選擇owner是誰,事後可以加上tag,每個裝置可以添加多個不同個tag,也可以多個裝置都使用一個tag

完整的ACLs說明可以參考官網說明https://headscale.net/stable/ref/acls/
備份
添加的內容想要備份,將來在其他的容器使用,則可以
sudo tar -czvf headscale-backup-$(date +%Y%m%d).tar.gz /home/icka/headscale
然後將壓縮檔備份起來