在幾次旅途中,我試圖將新拍的照片上傳到長沙家中的 Immich 伺服器。過去我用 Cloudflare Tunnel 做反代,但由於國內特殊的網絡環境,連線慢速、頻繁中斷、上傳失敗幾乎成了常態。後來我開始嘗試 Tailscale —— 一個基於 WireGuard 的內網穿透工具,它終於讓我在全國各地都能穩定、高速地訪問家中的 NAS 和照片庫。
但新的問題也隨之而來:Tailscale 在 Android 手機上執行時,需要作為 VPN 服務接管系統流量,而我平時使用的 Clash 同樣依賴 VPN 介面進行分流。由於 Android 系統限制(只能啓用一個 VPN 服務),兩者無法共存,導致我必須在「訪問家中服務」和「科學上網」之間二選一。
這篇文章從 Tailscale 的原理講起,到自建 DERP 伺服器最佳化連線質量,再到如何用 iptables 劫持 Tailscale Exit Node 的流量並轉發給 ShellCrash,實現流量的靈活轉發與安全穿透。無論你是想訪問家庭區域網上的 NAS、照片庫,還是希望在陌生網絡中保護自己的資料安全,這篇文章都能為你提供一套實用、穩定的解決方案。
什麼是 Tailscale
Tailscale 是一款基於 WireGuard 協議的零配置虛擬區域網工具,它能夠讓分佈在不同網絡環境中的裝置像處於同一個安全內網中一樣互聯互通。透過自動穿透 NAT、防火牆等網絡障礙,Tailscale 讓你無需公網 IP、無需埠轉發,也能輕鬆訪問家中的 NAS、個人伺服器、開發環境等內網資源。它的核心優勢在於簡單、安全、穩定,啓動即用,資料傳輸全程加密,適合個人開發者、遠端辦公者、家庭用戶等多種場景使用。
Tailscale 的技術實現非常巧妙:其構建在 WireGuard 加密協議之上,卻顛覆了傳統 VPN 的 IP 分配邏輯。每個裝置透過 SSO/OAuth2 完成身份認證後,會獲得一個終身繫結的節點金鑰。這種基於身份的組網模式,讓「長沙的 NAS」和「香港的手機」在虛擬網絡中如同辦公室同事般直接對話。
Tailscale 的連線過程原理
中心控制伺服器(Control Server)
每個 Tailscale 客户端在啓動後,首先會連線控制伺服器(controlplane),進行身份驗證,並拉取整個網絡中其他節點的資訊,包括每台裝置的公網 IP、埠、NAT 型別等。這一步相當於是「認識朋友」。
Tailscale 的控制伺服器不會轉發任何資料,只負責協調連線 —— 類似一個排程中心。
DERP 伺服器
説到 Tailscale 能保持高連線成功率的關鍵,就不得不提到 Tailscale 自研的中轉協議 DERP,在 Tailscale 的網絡架構裏,DERP(Designated Encrypted Relay for Packets)是一個很重要但通常只在必要時介入的元件。簡單來説,它就是一個基於 HTTP 的加密中繼伺服器,用來在兩台裝置無法直接通訊時,作為它們之間的「中轉站」。
所有客户端之間的連線都是先選擇 DERP 模式(中繼模式),這意味着連線立即就能建立,用戶無需等待。然後連線雙方開始並行地進行路徑發現,通常幾秒鐘之後,Tailscale 就能發現一條更優路徑,然後將現有連線透明升級(upgrade)過去,變成點對點連線(直連)。1
需要注意的是:
- 所有透過 DERP 的資料都是端到端加密的,DERP 伺服器無法檢視內容;
- Tailscale 會盡可能少地使用 DERP,一旦直連建立成功,就會自動切換過去;
- 官方部署了多個分散式 DERP 節點,客户端會自動選擇延遲最低的那個;
- 你也可以自建 DERP 節點(比如在國內),來解決延遲高或連線不穩定的問題。
可以把 DERP 理解為一個兜底機制,雖然效能不如直連,但確保了即便不能打洞成功,裝置之間也始終能保持連線。
NAT 穿透(NAT Traversal)
拿到對端的地址資訊後,Tailscale 會嘗試透過 NAT 穿透來建立點對點(P2P)連線。這個過程使用了 STUN 協議,雙方互相傳送探測包,嘗試在 NAT 路由器上打出一條直連的通道。如果雙方的網絡條件允許,就可以成功建立起一個 UDP 的直連隧道,資料走直連,速度快、延遲低。
受制於篇幅,我無法完整細緻的講述 NAT 穿透的原理,若對這部分感興趣,可以閲讀 Tailscale 官方的「How NAT traversal works」一文。
完整連線流程圖示
自建 DERP
Tailscale 的安裝在各個平台上都相對簡單,官方文件已經提供了詳細的操作指南。本文將不再贅述安裝過程,以下內容預設你已經在相關裝置上成功安裝並登入了 Tailscale。
為什麼要自建 DERP?
Tailscale 在全球部署了眾多 DERP 2中繼伺服器,用於在打洞失敗時接管流量中轉。但由於眾所周知的原因,中國大陸並沒有官方部署的 DERP 節點。這意味着:
- 一旦 NAT 打洞失敗,所有流量都必須繞行海外的 DERP 節點,延遲高、速度慢,體驗極差;
- 某些官方 DERP 節點容易被 GFW 干擾,可能出現連線中斷、握手失敗等問題;
- 即使打洞成功,Tailscale 仍需透過 DERP 交換路由資訊和 WireGuard 金鑰,如果 DERP 不可達,連線質量也會受到影響。
因此,在國內網絡環境下,自建一個本地 DERP 節點,不僅可以顯著提高連線穩定性和傳輸效能,還能規避部分網絡封鎖所帶來的不可預期問題,是一個非常值得做的最佳化。
準備工作
前面提到,DERP 是基於 HTTP 的,所以你需要準備好一個 HTTP 反代服務,並自行解決 SSL 證書的簽發等基礎問題。本文使用 Docker 部署,在部署開始之前,你需要在你的伺服器上裝好 Docker 以及 Docker Compose 等附加元件。為了編輯配置檔案,你當然也得知道如何使用 nano 之類的編輯器。為了最好的效果,你的伺服器最好擁有靜態公網 IPv4+IPv6 雙棧地址。
如果以上條件都具備,就可以開始部署了。
| |
docker-compose.yml
| |
network_mode: host是一個關鍵配置,表示容器將共享宿主機的網絡棧。如果使用 Docker 預設的 bridge 網絡模式,容器的網絡會經過 Docker 內網轉發,會造成 DERP 的 STUN 服務識別到 Docker 172.17.0.0/16網段下的地址,而
無法識別到客户端正確的外網地址,導致 Tailscale 客户端無法正確連線。
| |
前面提到所有透過 DERP 的資料都是端到端加密的,DERP 並不知道是誰在使用,這意味着如果不採取措施,任何知道你 DERP 伺服器地址和埠號的人都可以使用它。這條配置的作用是掛載宿主機的 Tailscale 套接字檔案到容器內,目的是允許 derper 服務透過 Tailscale 的 tailscaled 服務進行身份驗證。配合DERP_VERIFY_CLIENTS=true,可以防止你的 DERP 節點被他人白嫖。
需要注意的是:
- 宿主機必須已經安裝並登陸 Tailscale 客户端(tailscaled),否則這個檔案不存在,容器會報錯;
- tailscaled 必須以 root 許可權執行,才能建立這個 sock 檔案。
反向代理
以 Caddy 為例,需要反向代理 4433 埠,併為其部署 SSL 證書。
| |
需要注意的是,如果你和我一樣使用 Caddy 配合 dnsproviders 申請泛域名證書,Tailscale 的 MagicDNS 可能會導致 Caddy 本地證書驗證失敗而報錯,需要手動指定resolvers引數解決。
訪問剛剛反代的節點,如果出現以下頁面,説明配置正確。

配置 ACL 策略
開啓 Tailscale 控制枱的「Access Controls」頁面配置 ACL 策略,將配置好的 DERP 加上。
Tailscale 的 ACL 策略是用 HuJSON3 寫的,想要在 VSCode 中編輯,選擇語言為「JSON with Comments(jsonc)」即可。以下是一個配置示例:
| |
Tailscale 保留RegionID中的 1-899 作為官方節點,自建節點的 RegionID 必須大於等於 900。
測試連線
配置好 ACL 並儲存,Tailscale 會自動為所有客户端同步配置,稍等片刻在客户端用tailscale netcheck測試連線。

需要注意返回的 IP 是否是自己真實的公網 IP,若返回了172.17.0.0/16網段的地址,説明你 Docker 部分配置錯了。
與科學上網並存:在 Exit Node 劫持流量到 Clash 核心
宣告 Exit Node
在家中準備一個 24 小時開機的裝置,可以是樹莓派,可以是 MacMini。在上面安裝 Tailscale 並將其宣告為 Exit Node,並根據需要在 Tailscale 內網宣告家庭內網網段,隨後在控制枱啓用這個裝置作為 Exit Node,你就獲得了一個免費的 VPN,可以讓你在陌生的網絡環境中保持安全。
| |

廣播完成後,無論身處何地,只要能連上 Tailscale 網絡,就能訪問家中所有的內網裝置。
啓用 IP 轉發和禁用 UDP GRO4
啓用 IP 轉發是樹莓派等裝置作為 Exit Node 所必須的配置,這裏以樹莓派為例,如果你使用其他裝置,請自行查閲 Tailscale 官網教程。
| |
根據 Tailscale 官方的説法5,禁用 UDP GRO6 可以提升轉發效能,但官方的持久化教程似乎在樹莓派上無效,好在我們可以手動配置。
| |
針對持久化的問題手動編寫 systemd 配置檔案:
| |
檔案內容如下:
| |
隨後啓動服務:
| |
在 Exit Node 劫持流量
我的 Exit Node 是一台樹莓派,在樹莓派上配置好 Exit Node 之後就來到了最後一步,也就是劫持手機傳送到 Exit Node 上的流量,實現科學上網。出於穩定性原因,我不希望在家庭主路由上直接執行代理軟件,為了實現這一點,直接在樹莓派上劫持流量是唯一的選擇。
首先安裝 ShellCrash,請自行根據你的需要匯入配置檔案、配置自動任務等:
| |
隨後啓動服務,修改修改防火牆執行模式為純淨模式,我個人建議將 SNI 嗅探開啓,並將 DNS 模式從fake-ip切換為redir-host7,同時啓用 IPv6 透明代理。

設定純淨模式的目的是手動配置 iptables 以實現更精準的流量劫持。我們直接用 iptables 劫持所有 tailscale 網絡卡作為 Exit Node 轉發的流量,首先用ifconfig檢視 tailscale 網絡卡的名稱,預設情況下一般為 Tailscale 0:
| |
劫持tailscale0網絡卡的所有流量到本地 Clash 核心監聽埠7892,這個設定在 ShellCrash 中叫做「靜態路由埠」。以及,如果你不想和我一樣遇到莫名其妙的網絡問題,就一定不要忘記劫持 IPv6。
| |
進階:用 TProxy 劫持 UDP 流量
iptables 的 REDIRECT 只能重定向 TCP 流量,UDP 沒有連線狀態(無連線協議),所以 REDIRECT 無法保留目標地址,導致透明代理無法知道原始目標地址。
所以,如果你用如下方式劫持 UDP 流量:
| |
則這個規則不會生效或代理行為不正常。
那麼,有辦法代理 UDP 流量嗎?有的兄弟,有的。但前提是:
- 核心支援 UDP 透明代理(Clash Premium 和 Mihomo 都支援);
- 使用 TProxy 模式,而不是 REDIRECT;
- 正確配置了 iptables mangle 表和 policy routing;
- 代理配置檔案中啓用了 UDP 代理(如 mode: rule + udp: true)。
假設你已經滿足第一條、第二條和最後一條,則以下是一個示例8:
| |
當然,不要忘記 IPv6:
| |
路由規則持久化
ip rule和ip route建立的規則在重啓後會丟失,所以需要我們手動持久化,最簡單直接的方法就是建立一個指令碼,並將其新增至 crontab。
建立一個指令碼:
| |
編輯為如下內容:
| |
授予執行許可權後編輯 Crontab:
| |
在 crontab 檔案底部加上如下內容:
| |
原理解釋:TProxy 到底是怎麼轉發 UDP 流量的?
如果你看到這裏,也許會產生疑惑:為什麼整個流程中,我們沒有在 iptables 裏寫--to-ports,也沒看到目標地址被改寫,UDP 流量就莫名其妙地被代理了?這是怎麼做到的?
要解釋這個問題,我們先來看 TProxy 和 REDIRECT 的根本區別:
REDIRECT 模式:
- 使用 iptables nat 表;
- 將目標地址改寫為本地地址(比如 127.0.0.1:7892);
- 通常用於 TCP 流量;
- 不能保留真實目標地址;
- 需要指定
--to-ports。
TPROXY 模式:
- 使用 iptables mangle 表;
- 不修改目標 IP,而是保留原始目標地址;
- 透過 fwmark 和 policy routing 將報文路由到
lo; - 代理程式監聽一個特殊埠(例如 7893),並啓用
IP_TRANSPARENT; - 支援 UDP 和 TCP;
- 不需要 iptables 內指定
--to-ports,因為不是 NAT,而是標記 + 路由。
TProxy 不使用 DNAT/REDIRECT,而是透過 mangle 表給資料包打上 mark,然後透過 policy routing(ip rule + ip route)將這些資料包送到 lo 介面。代理程式(如 Clash / ShellCrash)監聽在 lo9 上的埠(例如 7893),透過啓用 IP_TRANSPARENT 選項,可以讀取資料包的原始目標 IP 和埠並進行代理轉發。
簡而言之,TProxy 模式只需要:
iptables給資料包打上 mark;ip rule+ip route將這些包送到lo;- 程式監聽
lo上的埠並開啓IP_TRANSPARENT。
所以不需要在 iptables 中指定 --to-ports,因為目標 IP 和埠保持不變,代理程式自己可以感知並處理。
iptables 規則持久化
安裝iptables-persistent:
| |
安裝過程中會提示你是否儲存當前的 IPv4 和 IPv6 配置,選擇「是」即可。之後如果你新增了新的規則,記得執行儲存命令:
| |
儲存後的規則檔案路徑:
- IPv4:
/etc/iptables/rules.v4 - IPv6:
/etc/iptables/rules.v6
你也可以直接編輯上面的rules.v4/rules.v6檔案,按需修改。
最終效果

這套方案的使用體驗取決於你家的上行頻寬,我家的網絡是下行 500M 上行 60M,目前沒有遇到一次打洞失敗的情況,所以基本都能跑滿,延遲也尚可接受,並且可以在外地隨時隨地端到端加密訪問家中的 Immich 和 OpenWRT 路由器等裝置以及實現科學上網,總體來看,我還算比較滿意。
有關 HuJSON: https://github.com/tailscale/hujson ↩︎
蔘考: https://tailscale.com/kb/1320/performance-best-practices#linux-optimizations-for-subnet-routers-and-exit-nodes ↩︎
UDP GRO(Generic Receive Offload)是 Linux 核心中的一種網絡最佳化技術,主要用於合併多個小資料包以提高處理效率。但在裝置作為網絡轉發節點的使用場景下,這可能會導致轉發延遲增加和高丟包率環境下的吞吐量下降。 ↩︎
相較於
fake-ip,redir-host相容性更好,出現問題的機率更低,也不會出現開關代理之後短時間內因為fake-ip殘留而斷網的情況,所以一般情況下我建議使用redir-host搭配 GeoSite 分流規則使用。我這台樹莓派的 DNS 上游是已經配置好的 SmartDNS,不存在 DNS 污染的問題,體驗良好。 ↩︎蔘考: https://blog.zonowry.com/posts/clash_iptables_tproxy/ ↩︎
lo是 Linux 系統中預設的「迴環介面(Loopback Interface)」,在透明代理中,它不僅處理 localhost 流量,還被用來接收原本屬於外部世界的網絡連線,實現對外部流量的本地劫持和轉發。 ↩︎

評論已停用,直到您接受功能性 Cookie。