Tailscale 筆記
前言
想從外面連回家裡的 NAS、路由器管理頁、或是那台跑著一堆服務的小主機,傳統做法是在路由器上開通訊埠轉發 (Port forwarding)、自架 OpenVPN 或 WireGuard,然後跟浮動 IP、NAT、防火牆規則搏鬥半天。
Tailscale 把這件事變成「裝一個軟體、登入、結束」。它底層就是 WireGuard,但把最麻煩的金鑰交換、NAT 穿透、裝置探索全部自動化了。你不需要開任何對外通訊埠,裝置之間會自己想辦法直連。
這篇是我的設定筆記,涵蓋安裝、常用指令、子網路由 (Subnet Router)、出口節點 (Exit Node)、Tailscale SSH、MagicDNS、Tailscale Services,以及 Docker 部署方式。
核心概念
先把名詞搞清楚,後面看設定會順很多。
| 名詞 | 意思 |
|---|---|
| Tailnet | 你的私有網路,一個帳號一個 tailnet,所有裝置都在裡面 |
| 節點 (Node) | 加入 tailnet 的裝置,每台會拿到一個 100.x.y.z 的固定 IP |
| 控制平面 (Control Plane) | Tailscale 的協調伺服器,負責交換公鑰與裝置清單。不處理你的流量 |
| DERP | 中繼伺服器。直連失敗時流量走這裡,仍然是端對端加密 |
| 子網路由 (Subnet Router) | 讓一台節點代理整個內網網段,其他裝置不用每台都裝 Tailscale |
| 出口節點 (Exit Node) | 把所有對外流量都導到某個節點出去,等同傳統 VPN 的用法 |
| MagicDNS | 自動幫每台節點配一個名字,直接用 hostname 連線 |
| Service | 與裝置解耦的具名服務,有自己的虛擬 IP (TailVIP) 與 MagicDNS 名稱 |
| ACL / Tailnet Policy File | 存取控制規則,用 JSON 描述誰能連到誰 |
控制平面只負責交換金鑰與裝置資訊,實際流量是節點之間直接走 WireGuard 加密隧道。就算 Tailscale 的伺服器被入侵,對方也解不開你的流量內容。不放心的話可以自架 Headscale 取代控制平面。
安裝
Linux
主流發行版(Ubuntu、Debian、RHEL、CentOS、Fedora、Raspberry Pi OS、Amazon Linux、openSUSE、Oracle Linux)直接用官方腳本:
curl -fsSL https://tailscale.com/install.sh | sh
裝完啟動並登入:
sudo tailscale up
終端機會印出一個授權網址,用瀏覽器開啟並登入即可。裝置隨即出現在後台的 Machines 頁面。
確認狀態:
tailscale ip # 看自己的 tailnet IP
tailscale status # 看所有節點與連線方式
tailscale status 的輸出裡要特別注意最後一欄:direct 代表 P2P 直連,relay "xxx" 代表流量正在走 DERP 中繼。
其他平台
| 平台 | 安裝方式 |
|---|---|
| Windows | 官方 .exe / winget |
| macOS | App Store 版或獨立版 .pkg |
| iOS / iPadOS | App Store |
| Android | Google Play |
| Synology / QNAP | 官方套件(Synology 需從 Tailscale 官網下載 .spk) |
| OPNsense / pfSense | 內建套件庫 |
macOS 有兩種版本:App Store 版跟獨立版 (Standalone)。獨立版才支援出口節點與子網路由等進階功能,App Store 版功能受 sandbox 限制。要當 Exit Node 的話請裝獨立版。
常用指令
tailscale status # 節點清單與連線狀態
tailscale ip -4 # 只看 IPv4
tailscale ping <node> # 測試對某節點的連線路徑(會顯示 direct 還是 relay)
tailscale netcheck # 檢查本機 NAT 型態與各 DERP 延遲
tailscale down # 斷線但保留登入狀態
tailscale up # 重新連線
tailscale logout # 登出並從 tailnet 移除
tailscale file cp <file> <node>: # 傳檔案到另一台節點
tailscale bugreport # 產生診斷識別碼,回報問題時用
tailscale ping 跟系統的 ping 不一樣。它會沿著 WireGuard 路徑測試,並告訴你這條連線是直連還是走中繼,排查速度問題的第一個工具就是它。
set 與 up 的差別
新版 Tailscale 把「修改設定」跟「連線」拆開了:
tailscale up:連線。加上旗標時會重設未指定的設定tailscale set:只改指定的那一項設定,其他保持不動
所以要調整既有節點的設定,用 tailscale set 比較安全,不會意外把之前的旗標清掉。
子網路由 (Subnet Router)
情境:家裡有一台伺服器、一台 NAS、一台路由器、幾台 IoT 裝置。不可能每台都裝 Tailscale(IoT 裝置根本裝不了),這時候就在其中一台 Linux 機器上開子網路由,讓它代理整個 192.168.1.0/24。
1. 開啟 IP 轉送
這步很容易忘,忘了就是「設定看起來都對但連不通」。
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
沒有 /etc/sysctl.d/ 目錄的系統,寫進 /etc/sysctl.conf 再 sudo sysctl -p /etc/sysctl.conf。
2. 宣告路由
sudo tailscale set --advertise-routes=192.168.1.0/24
多個網段用逗號分隔:--advertise-routes=192.168.1.0/24,10.0.0.0/16。
3. 在後台核准路由
宣告完不會馬上生效。到後台 Machines 頁面,該節點會出現 subnets 標記,點進去 → Subnets → Edit → 勾選要核准的路由 → Save。
想跳過手動核准,在 Policy File 裡設 autoApprovers:
{
"autoApprovers": {
"routes": {
"192.168.1.0/24": ["autogroup:admin"]
}
}
}
4. 用戶端接受路由
Linux 用戶端預設不接受子網路由,要手動開:
sudo tailscale set --accept-routes
Windows、macOS、iOS、Android 會自動探索路由,不用額外設定。
5. 設定存取控制
Policy File 裡授權誰可以連進這個網段:
{
"grants": [{
"src": ["group:dev"],
"dst": ["192.168.1.0/24"],
"ip": ["*:*"]
}]
}
保留來源 IP
預設子網路由會做 SNAT,內網機器看到的來源 IP 是子網路由那台。想保留原始來源 IP:
sudo tailscale up --snat-subnet-routes=false
代價是內網那些機器需要有一條回到 100.64.0.0/10 的路由,否則封包出不去。通常要在路由器上加靜態路由。
子網路由那台機器如果關機,整個網段就都連不到了。重要環境建議開兩台做高可用:兩台宣告相同的路由並都核准,Tailscale 會自動容錯切換。
出口節點 (Exit Node)
子網路由是「連進特定網段」,出口節點是「所有對外流量都從那台出去」,等於傳統 VPN 的用法。適合在外面用公共 Wi-Fi 時把流量繞回家裡。
宣告為出口節點
前提一樣要先開 IP 轉送(見上一節)。
sudo tailscale set --advertise-exit-node
然後到後台該節點的設定裡核准 exit node。用 firewalld 的系統要額外允許 masquerade:
sudo firewall-cmd --permanent --add-masquerade
使用出口節點
sudo tailscale set --exit-node=<節點名稱或IP>
問題來了:一旦啟用出口節點,連本地區域網路都會被導走,家裡的印表機就找不到了。要保留本地存取:
sudo tailscale set --exit-node=<節點名稱或IP> --exit-node-allow-lan-access=true
關閉:
sudo tailscale set --exit-node=
Policy File 需要授權 autogroup:internet:
{
"grants": [{
"src": ["group:dev"],
"dst": ["autogroup:internet"],
"ip": ["*:*"]
}]
}
每台裝置都必須主動選擇要使用出口節點,不會被強制套用。這是刻意的設計——避免管理員一改設定就把所有人的流量全部繞道。
MagicDNS
啟用後每台節點自動獲得 <hostname>.<tailnet-name>.ts.net 的名字,同一個 tailnet 內直接用 hostname 就能連,不用背 100.x.y.z。
後台 DNS 頁面 → 勾選 Enable MagicDNS。
Linux 用戶端要接受 DNS 設定:
sudo tailscale set --accept-dns
改主機名稱:
sudo tailscale set --hostname=nas
MagicDNS 會接管 /etc/resolv.conf。跟本機已經在跑的 DNS 服務(Pi-hole、AdGuard Home、dnsmasq、systemd-resolved 的某些設定)容易打架。
如果那台機器本身就是你的 DNS 伺服器,建議關掉該節點的 --accept-dns,改在後台 DNS 頁面把它設成 tailnet 的 Global nameserver——這樣所有節點都能用你的 Pi-hole 過廣告。
Tailscale SSH
Tailscale 可以接管 port 22,用 tailnet 身分驗證取代 SSH 金鑰。好處是不用再手動散發公鑰、離職成員的存取權在後台一鍵撤銷。
目標主機上啟用:
sudo tailscale set --ssh
Policy File 加上 ssh 區塊(沒有這段的話連不上):
{
"ssh": [{
"action": "accept",
"src": ["autogroup:member"],
"dst": ["autogroup:self"],
"users": ["autogroup:nonroot", "root"]
}]
}
連線就跟平常一樣:
ssh nas
ssh ubuntu@nas
action 除了 accept 還可以設 check,會要求連線者先在瀏覽器完成一次身分再驗證,適合用在生產環境的機器上。
啟用 Tailscale SSH 後,從 tailnet 進來的 22 埠流量會被 tailscaled 攔截,不再交給 sshd。設定 ACL 之前先確認你還有另一條進入路徑(本機終端機、實體存取、或走非 tailnet IP 的 SSH),不然 ACL 寫錯就把自己鎖在門外了。
Serve 與 Funnel
Serve:把本機服務用 HTTPS 分享給 tailnet 內部,自動配憑證。
tailscale serve --bg 3000 # 把 localhost:3000 掛到 https://<host>.<tailnet>.ts.net
tailscale serve status # 看目前設定
tailscale serve reset # 清掉全部設定
Funnel:把服務開放到整個網際網路,不需要用戶端。
tailscale funnel --bg 3000
tailscale funnel status
tailscale funnel off
Funnel 是真的對全世界開放,Tailscale 不會幫你加任何驗證。只適合本來就打算公開的東西(webhook 接收端、給別人看的 demo)。管理後台、資料庫、任何有敏感資料的服務都不要用 Funnel。
Tailscale Services
Serve 有個先天限制:服務綁在裝置上。網址是 https://nas.<tailnet>.ts.net,機器換掉、服務搬家,網址就跟著變。
Tailscale Services 解決的就是這件事——把服務從裝置上剝離,變成一個獨立的實體。每個 Service 會拿到自己的 TailVIP(虛擬 IP,IPv4 + IPv6 各一)與 MagicDNS 名稱 <service-name>.<tailnet>.ts.net,跟節點一樣是一等公民。底下由哪台機器提供服務、有幾台在提供,使用者完全不需要知道。
| 比較 | Serve | Services |
|---|---|---|
| 位址綁定 | 裝置的 MagicDNS 名稱 | 服務自己的 TailVIP 與名稱 |
| 搬遷 | 網址跟著變 | 網址不變 |
| 多台提供 | 不行 | 可以,自動導流與容錯 |
| ACL 目標 | tag:server 等節點識別 | svc:web-server |
| 適合 | 單機的個人服務 | 需要穩定位址、會搬遷或要做高可用的服務 |
需求:Tailscale v1.86.0 以上。提供服務的主機必須使用標籤身分 (tag),不能是綁在個人帳號下的節點——所以要先 tailscale up --advertise-tags=tag:server。
用戶端 v1.94 以上會自動探索 Services,不用設定。v1.93 以下的 Linux 用戶端要 sudo tailscale set --accept-routes。
1. 在後台定義 Service
Services → Advertise → Add Service,填入:
- 服務名稱(會變成
svc:<name>與 MagicDNS 名稱) - 說明
- 通訊埠與通訊協定,例如
tcp:443 - 選填標籤,用來在 ACL 裡分組
2. 在主機上設定端點
一行指令同時完成設定與宣告:
tailscale serve --service=svc:web-server --https=443 127.0.0.1:8080
HTTPS 端點的 TLS 憑證由 Tailscale 自動簽發與續期。
端點分三層,依需求選:
L7(應用層)——http / https。支援路徑分流、身分注入:
tailscale serve --service=svc:app --https=443 --set-path=/api localhost:8081
L4(傳輸層)——tcp / tls-terminated-tcp。純 TCP 轉送,不改封包,額外負擔比 L7 低,但不支援 --set-path:
tailscale serve --service=svc:db --tcp:5432 tcp://localhost:5432
L3(網路層)——僅 Linux,不管通訊協定的 IP 轉送,保留封包邊界,可搭配 OS 層 iptables 設定支援 UDP:
tailscale serve --service=svc:custom --tun
3. 後台核准
管理員到 Services 頁面把待審的宣告按 Approve。要免手動核准,在 Policy File 設 autoApprovers:
{
"autoApprovers": {
"services": {
"svc:web-server": ["tag:server"],
"tag:prod-service": ["tag:prod-infra"]
}
}
}
4. 設定存取控制
Service 可以直接當 dst,比對節點寫 ACL 乾淨很多:
{
"grants": [
{
"src": ["autogroup:member"],
"dst": ["svc:web-server"],
"ip": ["443"]
},
{
"src": ["tag:prod-service"],
"dst": ["svc:database"],
"ip": ["5432"]
}
]
}
設定檔方式
要管理多個 Service,或想把設定納入版控,用 JSON 設定檔比一行行敲指令好維護。
serveconfig.json:
{
"version": "0.0.1",
"services": {
"svc:web-server": {
"endpoints": {
"tcp:443": "https://localhost:443"
},
"advertised": true
}
}
}
| 欄位 | 必填 | 說明 |
|---|---|---|
version | 是 | 目前固定為 "0.0.1" |
services | 是 | 服務定義,鍵為 svc:<name> |
endpoints | 是 | 協定:通訊埠 對應到本機目標,例如 "tcp:443" |
advertised | 否 | 是否接受連線,預設 true |
套用與宣告:
tailscale serve set-config --all serveconfig.json
tailscale serve advertise svc:web-server
設定檔方式是「設定」與「宣告」兩步分離,比 CLI 一行搞定更可控——可以先把設定推上去,確認無誤再宣告。
維運指令
tailscale serve status --json # 看目前設定
tailscale serve drain svc:web-server # 排空:停收新連線,既有連線繼續處理
tailscale serve --service=svc:web-server --https=443 off # 移除單一端點
tailscale serve clear svc:web-server # 清掉該服務全部端點
tailscale serve advertise svc:web-server # 改完設定檔後重新宣告
確認本機是否為 service host:
tailscale status --json | jq '.Self.CapMap."service-host"'
服務狀態在後台可以看到:Pending approval(待核准)、Needs configuration(設定有誤或缺漏)、Connected(至少一台在提供)、Offline(沒有主機在宣告)、Pre-approved(已預先核准但還沒宣告)、Draining(排空中)。
高可用與滾動更新
多台主機宣告同一個 Service 時,Tailscale 會自動把流量導到可用的主機。想在同區域內做負載平衡,要另外在 tailnet 開啟 regional routing。
drain 讓滾動更新變得很單純:
# 在要下線維護的主機上
tailscale serve drain svc:web-server
# 等既有連線收乾,做你的維護
# 完成後重新宣告
tailscale serve advertise svc:web-server
Services 目前的限制:
- 只支援 TCP。UDP 僅能透過 Linux 的 L3 端點加上手動 OS 設定實現
- 只能接收連線。TailVIP 不能主動發起對外連線
- 保留封包邊界只有 Linux 有
- 設定檔不支援
text:與file:目標,那兩種只能用 CLI - 主機有多張網路介面、且遠端目標不在預設路由表裡時,Linux(缺少
SO_MARK)與 macOS App Store 版可能連不到遠端資源。變通做法是改用 userspace networking 模式
Docker 部署
官方映像檔是 tailscale/tailscale。有兩種常見用法。
事前準備:Auth Key
容器裡沒辦法互動式登入,要先在後台 Settings → Keys 產生一個 Auth Key。建議勾選:
- Reusable:需要重建容器時不用一直換 key
- Ephemeral:容器離線後自動從 tailnet 移除節點,避免後台一堆殭屍裝置
- Tags:綁定
tag:container之類的標籤,方便寫 ACL
用法一:獨立節點 + 子網路由
讓容器本身當一個節點,順便代理 Docker 網段。
services:
tailscale:
image: tailscale/tailscale:latest
hostname: docker-host
environment:
- TS_AUTHKEY=tskey-auth-xxxxx
- TS_STATE_DIR=/var/lib/tailscale
- TS_ROUTES=172.20.0.0/16
- TS_EXTRA_ARGS=--advertise-exit-node
- TS_USERSPACE=false
volumes:
- ./tailscale-state:/var/lib/tailscale
devices:
- /dev/net/tun:/dev/net/tun
cap_add:
- NET_ADMIN
- SYS_MODULE
restart: unless-stopped
用法二:Sidecar 模式
讓某個服務只透過 Tailscale 存取,完全不開對外通訊埠。關鍵是 network_mode: service:tailscale,讓服務共用 Tailscale 容器的網路命名空間。
services:
tailscale:
image: tailscale/tailscale:latest
hostname: my-app
environment:
- TS_AUTHKEY=tskey-auth-xxxxx
- TS_STATE_DIR=/var/lib/tailscale
- TS_SERVE_CONFIG=/config/serve.json
volumes:
- ./tailscale-state:/var/lib/tailscale
- ./serve.json:/config/serve.json:ro
restart: unless-stopped
app:
image: nginx:alpine
network_mode: service:tailscale
depends_on:
- tailscale
restart: unless-stopped
serve.json 讓 Tailscale 直接處理 HTTPS:
{
"TCP": { "443": { "HTTPS": true } },
"Web": {
"${TS_CERT_DOMAIN}:443": {
"Handlers": { "/": { "Proxy": "http://127.0.0.1:80" } }
}
}
}
這樣 app 完全沒有對外通訊埠,只有 tailnet 內連得到 https://my-app.<tailnet>.ts.net。
用法三:官方 Docker Compose 範例
官方文件的入門寫法,最單純:Tailscale 容器獨立跑,服務容器照舊 publish 埠到 host。
步驟:
- 到後台 Settings → Keys 產生 Auth Key,複製起來(只會顯示一次)
- 建立
docker-compose.yml,把<tskey-YOUR-AUTH-KEY>換成剛剛的金鑰 docker compose up -d- 到後台 Machines 確認
tailscale-nginx已加入 tailnet - 從 tailnet 上任一裝置連 Docker host 的
8080埠
services:
tailscale:
image: tailscale/tailscale:latest
container_name: tailscale
hostname: tailscale-nginx
environment:
- TS_AUTHKEY=<tskey-YOUR-AUTH-KEY>
- TS_STATE_DIR=/var/lib/tailscale
volumes:
- ./tailscale-state:/var/lib/tailscale
cap_add:
- net_admin
- net_raw
restart: unless-stopped
nginx:
image: nginx:latest
container_name: nginx_server
ports:
- "8080:80"
depends_on:
- tailscale
restart: unless-stopped
只有兩個環境變數:TS_AUTHKEY 認證、TS_STATE_DIR 搭配 volume 保存狀態。net_admin 與 net_raw 讓容器能建立 tun 介面走核心態網路,不加的話只能跑 userspace 模式,效能較差。
這個範例裡 nginx 是用 ports 開在 Docker host 上的,跟 Tailscale 容器沒有共用網路。也就是說:它同時對 host 所在的實體區域網路開放,不是只有 tailnet 連得到。
拿來熟悉流程很好,正式用要收緊的話:
- 想要只有 tailnet 連得到 + 自動 HTTPS → 改用用法二的 sidecar
- 想讓 tailnet 直接連容器 IP、完全不開 host 埠 → 改用用法一的子網路由
| 用法 | 曝露範圍 | 位址 | 適合 |
|---|---|---|---|
| 一:獨立節點+子網路由 | 只有 tailnet | 容器 IP,例如 172.20.0.5:8080 | 服務多、想省節點數 |
| 二:Sidecar | 只有 tailnet | https://<name>.<tailnet>.ts.net | 要 HTTPS、要穩定名稱 |
| 三:官方入門範例 | tailnet + host 區網 | Docker host IP + 埠 | 練習、內網本來就可信任 |
官方的範例集在 tailscale-dev/docker-guide-code-examples,除了 nginx 還有 OAuth 認證、Mealie、Immich(含 GPU)、Actual Server 等完整範例,也附了一份 example-acls.hujson。Podman 可以直接套用不用改。
常用環境變數
| 變數 | 用途 | 預設值 |
|---|---|---|
TS_AUTHKEY | 加入 tailnet 的授權金鑰 | 未設定 |
TS_HOSTNAME | 節點在 tailnet 的名稱 | 自動產生 |
TS_STATE_DIR | 狀態存放目錄,一定要掛 volume | 未設定 |
TS_ROUTES | 宣告子網路由 | 未設定 |
TS_USERSPACE | 使用者空間網路模式 | true |
TS_ACCEPT_DNS | 接受後台的 DNS 設定 | 不接受 |
TS_SERVE_CONFIG | Serve / Funnel 設定的 JSON 檔 | 未設定 |
TS_AUTH_ONCE | 只認證一次,而非每次啟動都認證 | false |
TS_DEST_IP | 把進來的 tailnet 流量全部轉給某個 IP | 未設定 |
TS_TAILNET_TARGET_IP | 反向:把流量代理到 tailnet 內某個 IP | 未設定 |
TS_TAILNET_TARGET_FQDN | 同上,用 MagicDNS 名稱指定 | 未設定 |
TS_SOCKS5_SERVER | 開 SOCKS5 代理的位址與通訊埠 | 未設定 |
TS_ENABLE_HEALTH_CHECK | 開啟 /healthz 端點 | 未啟用 |
TS_EXTRA_ARGS | 附加給 tailscale up 的旗標 | 未設定 |
TS_TAILSCALED_EXTRA_ARGS | 附加給 tailscaled 的旗標 | 未設定 |
TS_EXPERIMENTAL_SERVICE_AUTO_ADVERTISEMENT | 啟動時自動宣告 Tailscale Services | true |
TS_STATE_DIR 沒有掛 volume 的話,每次重建容器都會被視為全新裝置重新加入 tailnet。後台會累積一堆同名節點(my-app-1、my-app-2…),MagicDNS 名稱也跟著跑掉。
TS_USERSPACE 預設是 true,這種模式不需要 NET_ADMIN 與 /dev/net/tun,但效能較差、也不能當子網路由或出口節點。要做這兩件事就得設 TS_USERSPACE=false 並補上 cap_add 與 devices。
容器提供 Service
Sidecar 模式搭配 Tailscale Services 是很順的組合——容器本來就是會被重建、會換機器的東西,正好需要一個與裝置解耦的位址。
把 TS_SERVE_CONFIG 指向 Service 格式的設定檔即可:
{
"version": "0.0.1",
"services": {
"svc:web-server": {
"endpoints": {
"tcp:443": "http://127.0.0.1:80"
}
}
}
}
容器啟動時會自動宣告(由 TS_EXPERIMENTAL_SERVICE_AUTO_ADVERTISEMENT 控制,預設 true)。Auth Key 記得綁 tag——Service 主機必須是標籤身分。
同一個 Service 在多台機器上跑相同的容器,就直接得到高可用:滾動更新時逐台 tailscale serve drain,位址不變、使用者無感。
存取控制 (ACL)
預設的 Policy File 是全部開放——tailnet 內任何裝置都連得到任何裝置。只有自己用還行,多人或多用途的環境該收緊。
一個實際的例子:
{
"tagOwners": {
"tag:server": ["autogroup:admin"],
"tag:container": ["autogroup:admin"]
},
"grants": [
{
"src": ["autogroup:admin"],
"dst": ["*"],
"ip": ["*:*"]
},
{
"src": ["group:family"],
"dst": ["tag:server"],
"ip": ["tcp:8096"]
}
],
"groups": {
"group:family": ["[email protected]", "[email protected]"]
}
}
這份規則的意思是:管理員能連所有東西,家人只能連到帶 tag:server 標籤的機器的 8096 埠(Jellyfin),其他一律拒絕。
節點加標籤:
sudo tailscale up --advertise-tags=tag:server
加了標籤的節點所有權會轉移給標籤,不再屬於原本的使用者,也不會受該使用者的權限影響。伺服器類的節點建議都打標籤,這樣 ACL 才寫得乾淨,而且成員離職時不會連帶影響伺服器。
另外注意:加標籤需要重新認證,tailscale set --advertise-tags 不能用,要用 tailscale up。
常見問題
連線一直走 relay 不是 direct
tailscale netcheck 看自己的 NAT 型態。兩邊都是對稱式 NAT (symmetric NAT) 的話打洞會失敗,只能走中繼。改善方式:
- 路由器關掉 UPnP 以外的嚴格 NAT 設定,或改用 Full Cone NAT
- 在其中一邊設定固定的 WireGuard 通訊埠並做通訊埠轉發:
tailscaled --port=41641 - 有一台有公開 IP 的機器就讓它當節點,變成事實上的直連對象
忘記開 IP 轉送
子網路由與出口節點最常見的問題。tailscale status 看起來一切正常、節點也 ping 得到,但流量就是過不去。檢查:
sysctl net.ipv4.ip_forward # 要是 1
DNS 突然壞掉
MagicDNS 跟本機 DNS 服務衝突。先 sudo tailscale set --accept-dns=false 確認是不是這個原因。
後台一堆離線的殭屍節點
容器沒掛 state volume,或是 Auth Key 沒設 Ephemeral。後台手動刪掉,然後修設定。
結語
Tailscale 最大的價值是把「安全連回內網」這件事的設定成本壓到幾乎為零。不用開通訊埠、不用管憑證、不用處理浮動 IP,裝完登入就有一個私有網路。
實務上我的配置大致是:
- 家裡一台常開的 Linux 機器做子網路由 + 出口節點,涵蓋整個
192.168.1.0/24 - 那台同時是 Global nameserver,跑 AdGuard Home,所有節點連上就自動過濾廣告
- 需要單獨曝露的容器化服務用 Sidecar 模式 +
serve.json,完全不開對外通訊埠 - 會搬遷或要做高可用的服務改用 Tailscale Services,位址跟裝置脫鉤
- 伺服器節點全部打
tag:server,ACL 只放行必要的通訊埠 - 完全不用 Funnel,除非真的需要對外公開
不確定要不要開某項功能的時候,原則跟其他網路設定一樣:先關著,等真的需要再開。
參考資料
- Tailscale Quickstart
- Install on Linux
- Subnet routers
- Exit nodes
- MagicDNS
- Tailscale SSH
- Tailscale Serve
- Tailscale Funnel
- Tailscale Services
- Tailscale Services 設定檔
- tailscale serve 指令參考
- Four increasingly sophisticated ways to put a service on your tailnet
- Tailscale with Docker
- Connect a Docker container to your tailnet
- 官方 Docker 範例集
- Docker configuration parameters
- Tailnet policy file syntax
- Auth keys
- NAT traversal