把 Tailscale 裝在 OpenWrt 路由器上,整條區網的裝置(印表機、IP Cam、NAS、IoT)就能一次進 tailnet,也能讓路由器當 Subnet Router 或 Exit Node,出門在外照樣連回家。這篇講官方 feed 的套件、防火牆 zone 的設定,以及兩個最常見的角色怎麼落地。
本文依 2026 年 10 月的 Tailscale 官方文件與 OpenWrt 官方 wiki 整理,以 Tailscale v1.104、OpenWrt 25.12.5(2026 年 7 月 1 日發佈)為準;OpenWrt 25.12 官方 feed 的
tailscale套件版本為 1.102.3。本文未在實機刷機與安裝,屬文件整理。
套件:官方 feed 的 tailscale 與 luci-app-tailscale-community
OpenWrt 25.12 起,套件管理從 opkg 換成 apk(Alpine Package Keeper),安裝指令也跟著改:
apk update
apk add tailscale舊的 opkg update && opkg install tailscale 在 25.12 上已不適用。官方有 opkg 到 apk 的對照表可參考。
兩個套件的現況(依 openwrt/packages 的 openwrt-25.12 分支、openwrt/luci 的 openwrt-25.12 分支,以及 downloads.openwrt.org 25.12.5 的套件索引核對):
-
tailscale:官方 feed 收錄,25.12 的版本是 1.102.3-r1(上游穩定版此時已是 v1.104,feed 版本會落後幾個版本)。它把tailscaled與tailscale指令一起裝到/usr/sbin,並附/etc/config/tailscale設定檔與 procd 啟動腳本。套件的設定檔預設fw_mode 'nftables'——也就是說,官方套件預設就讓 tailscaled 用 nftables 管理防火牆規則,與 OpenWrt 22.03 起預設的 fw4/nftables 一致。 -
luci-app-tailscale-community:確實存在於官方 feed,而且是 25.12 起才收錄(24.10 分支查不到)。它提供 LuCI 網頁介面來查看狀態、設定節點與 daemon,維護者是 Tokisaki-Galaxy(上游為 luci-app-tailscale-community),並附繁中(luci-i18n-tailscale-community-zh-tw)與簡中翻譯套件。想用圖形介面就加裝:apk add luci-app-tailscale-community
想純指令操作,只裝 tailscale 就夠了;本文接下來的設定都以指令為主。
防火牆:給 tailscale0 專用 zone
Tailscale 建立自己的網路介面 tailscale0,但 OpenWrt 的防火牆(fw4)不會自動知道它,所以要在防火牆裡替它開一個專用 zone,否則 tailnet 的流量會被擋。官方 wiki 的步驟(LuCI 與 UCI 兩種寫法)如下。
建立 unmanaged 介面
先替 tailscale0 建立一個 unmanaged 介面(名稱就叫 tailscale),讓防火牆有東西可以認。UCI 寫法:
uci set network.tailscale=interface
uci set network.tailscale.proto='none'
uci set network.tailscale.device='tailscale0'
uci commit network對應 LuCI:Network → Interfaces → Add new interface,Protocol 選 Unmanaged、Device 選 tailscale0。官方 wiki 另外建議在該介面的 DHCP 設定裡按 Set up DHCP Server 再勾 Ignore interface(只為了在 dnsmasq 註冊這個介面、不實際發 DHCP),這一步主要影響 MagicDNS,見後文。
建立 firewall zone
接著建立 zone。關鍵選項(依 fw4 的 firewall 設定文件):
uci add firewall zone
uci set firewall.@zone[-1].name='tailscale'
uci set firewall.@zone[-1].network='tailscale'
uci set firewall.@zone[-1].input='ACCEPT'
uci set firewall.@zone[-1].output='ACCEPT'
uci set firewall.@zone[-1].masq='1'
uci set firewall.@zone[-1].mtu_fix='1'
uci commit firewall各選項的意義:
input ACCEPT/output ACCEPT:允許 tailnet 的裝置連到路由器本身(例如 SSH、LuCI),也允許路由器主動連出去。想鎖緊一點,可以把input改成REJECT再另外開放特定埠。masq '1':對從這個 zone 出去的流量做 masquerade(來源位址轉譯)。這讓 tailnet 來的封包轉進區網時,區網裝置看到的來源是路由器的區網 IP,回程封包才會走對。可以用masq_src把轉譯範圍限縮到特定來源網段(例如只對100.64.0.0/10做)。mtu_fix '1':開啟 MSS clamping。WireGuard 的封包比一般以太網略大,不做 MSS clamping 的話,某些 MTU 較小的連線(例如 PPPoE)會出現「小封包正常、大封包卡住」的症狀。官方 wiki 明確建議開啟。forward不用設:zone 的 forward 政策只在同一個 zone 內有多個介面時才有作用;這裡的tailscalezone 只涵蓋tailscale0一個介面,跨 zone 的放行由下一節的 forwarding 規則決定,所以官方 wiki 範例也把它留預設。
對應 LuCI:Network → Firewall → Zones → Add,Name 填 tailscale,Input/Output 設 ACCEPT,勾 Masquerading 與 MSS Clamping,Covered networks 選 tailscale,Forward 留預設。
forwarding:讓流量進出
zone 只是「分類」,實際放行要看 forwarding。依照角色加:
當 subnet router(tailnet → 區網):
uci add firewall forwarding
uci set firewall.@forwarding[-1].src='tailscale'
uci set firewall.@forwarding[-1].dest='lan'
uci commit firewall當 exit node(tailnet → 網際網路,要讓對外流量從這台出去):
uci add firewall forwarding
uci set firewall.@forwarding[-1].src='tailscale'
uci set firewall.@forwarding[-1].dest='wan'
uci commit firewall要讓區網裝置反過來連 tailnet 或走遠端 exit node(區網 → tailnet),再補一條 src='lan'、dest='tailscale':
uci add firewall forwarding
uci set firewall.@forwarding[-1].src='lan'
uci set firewall.@forwarding[-1].dest='tailscale'
uci commit firewall改完統一重載:
/etc/init.d/firewall reloadforwarding 是單向的,要雙向就各寫一條(src/dest 對調)。注意 fw4 reload 會印出解析結果,看到錯誤要先處理。
為什麼不要用舊教學的 –netfilter-mode=off
網路上的舊教學常要人加 --netfilter-mode=off,現在這不但不需要,反而該避免。
先講它做什麼。Tailscale 在 Linux 上會自己維護一套 netfilter 規則(放行 tailnet 流量、擋掉沒走 tailscale0 的 CGNAT 流量等)。--netfilter-mode 控制這個行為,有三個值(依官方 netfilter modes 文件與 v1.104 原始碼):
| 值 | 行為 |
|---|---|
on |
預設。Tailscale 建立並管理自己的規則(nftables 用獨立 table、掛在高優先權的 hook 上) |
nodivert |
建立規則但不啟用,由你自己決定何時把流量導進去 |
off |
完全不建立、不管理任何防火牆規則,全部要你自己寫 |
官方文件對 off 的態度很明確:「通常沒有好理由把 netfilter mode 設成 off」,只有 nodivert 也滿足不了的特殊需求才該用;設成 off 之後,原本 Tailscale 會自動建立的規則(包括放行 tailnet 流量的規則)都得你手動補齊。原始碼裡,除了 Synology 特例預設 off,其他平台預設都是 on。
舊教學為什麼這麼寫?OpenWrt 22.03 起就改用 fw4/nftables(fw3/iptables 是 21.02 以前的事),但 22.03 當時收錄的 tailscale 套件還不支援 nftables(Tailscale 到 v1.48 才加入 nftables 支援),無法自動設定防火牆規則,導致 tailscaled 初始化失敗、流量轉發異常。所以官方 wiki 的 Troubleshooting 才叫人加 --netfilter-mode=off,關掉 Tailscale 的 netfilter 管理、自己手動放行;官方 wiki 也記載這個問題在後續套件版本已修正,OpenWrt 23.0.5 之後不再需要這一步。現在官方套件預設就設 TS_DEBUG_FIREWALL_MODE=nftables(見 /etc/config/tailscale 的 fw_mode 選項),Tailscale 走 nftables、由 fw4 的 zone 統一管理,就沒有關掉它的理由了。
所以:別加 --netfilter-mode=off。照前一節建好 zone 與 forwarding,讓 Tailscale 維持預設的 on,防火牆交給 fw4 管就好。
–snat-subnet-routes=false 的意義
--snat-subnet-routes 控制 Tailscale 要不要對「轉往 subnet route 的流量」做來源位址轉譯(SNAT)。預設是 true:區網裝置看到的是 Subnet Router 本身的位址,回程自然走得回來。
官方 OpenWrt wiki 建議在路由器上加 --snat-subnet-routes=false,理由和前一節的 firewall zone 相扣:zone 已經開了 masquerade,fw4 會在封包轉進區網時做 NAT;如果 Tailscale 也做 SNAT,就變成兩層 NAT(雙重 NAT),既多餘、除錯時也容易搞混。關掉 Tailscale 的 SNAT,讓 fw4 的 masquerade 統一處理,是 OpenWrt 上比較乾淨的組合。
要留意的點:
- 這不表示會保留真實來源 IP。只要 zone 的
masq還開著,區網裝置看到的來源仍然是路由器的區網 IP。想要區網裝置看到 tailnet 對端真正的100.x.y.z位址,除了--snat-subnet-routes=false,還要把 zone 的masq關掉,並替區網加一條回程路由,把100.64.0.0/10指向路由器。完整說明見 Subnet Router 的 SNAT 一節。 - 和 exit node 衝突。同一台裝置同時當 exit node、又關掉 SNAT,可能造成上游丟包,官方建議把兩種角色拆開。見同篇的提醒。
- 這個旗標只有 Linux 支援(OpenWrt 是 Linux,沒問題)。
當 subnet router
把路由器所在的區網廣告進 tailnet,其他裝置就能存取區網內沒裝 Tailscale 的設備。假設區網是 192.168.1.0/24:
tailscale set --advertise-routes=192.168.1.0/24 --snat-subnet-routes=false
tailscale upOpenWrt 的核心預設已開啟 IP forwarding,不必像一般 Linux 那樣改 /etc/sysctl.conf(要確認的話:sysctl net.ipv4.ip_forward 應為 1)。接著到 Admin console 找到這台路由器,進 Edit route settings 核准網段,並建議替它 Disable key expiry(或改用 tag,見下文),路由器要常開。
區網網段不是 192.168.1.0/24 就換成你自己的。觀念與其他平台(--accept-routes、路由核准、用戶端設定)見 Subnet Router。
當 exit node
讓所有上網流量都從這台路由器出去:
tailscale set --advertise-exit-node
tailscale up依官方文件,--advertise-exit-node 用 tailscale set 下完,要再跑一次 tailscale up 才生效。接著在 Admin console 核准,其他裝置就能把它選為 exit node。詳情見 Exit Node。
同一台要兼當 subnet router 和 exit node 可以(保持 SNAT 預設開啟即可),但別再配
--snat-subnet-routes=false(見上一節的衝突提醒)。想要「關 SNAT 的 subnet router」又同時要 exit node,官方建議拆到兩台裝置。
存取規則與 autoApprovers
預設 policy 下,tailnet 裡所有人都能用你核准的 subnet router 和 exit node。要收緊,先替路由器打 tag(tagged 裝置預設金鑰不過期,也方便在 policy 裡指定):
tailscale up --advertise-tags=tag:openwrt--advertise-tags 是 tailscale up 的旗標(tailscale set 沒有這個選項)。打 tag 必須以該 tag 的 owner 身分重新認證才會生效:如果裝置已經用其他使用者登入,加上 --force-reauth 強制重新認證,或用帶 tag 的 auth key 登入;tag 也必須先在 policy 的 tagOwners 授權給你,否則會被拒絕。
然後在 tailnet policy file 裡寫最小權限。假設網段是 192.168.1.0/24:
{
"tagOwners": {
"tag:openwrt": ["autogroup:admin"]
},
"autoApprovers": {
"routes": {
"192.168.1.0/24": ["tag:openwrt"]
},
"exitNode": ["tag:openwrt"]
},
"grants": [
{
"src": ["autogroup:member"],
"dst": ["192.168.1.0/24"],
"ip": ["tcp:631"]
},
{
"src": ["autogroup:member"],
"dst": ["autogroup:internet"],
"ip": ["*"],
"via": ["tag:openwrt"]
}
]
}說明:
- 第一條 grant 放行「經由這台路由器連到區網網段」的印表機(subnet router):
ip用"tcp:631"只開放 IPP 列印埠。想開放整個網段所有埠,就把ip改成["*"]。 - 第二條放行「經由這台路由器上網」(exit node)。目的地要寫
autogroup:internet(不是裝置 IP),via指定只能走打上tag:openwrt的出口。via只接受 tag。 autoApprovers讓這台 tag 裝置廣告的網段與 exit node 自動核准,不用每次去後台點;它只在第一次廣告時生效,改 policy 不會回頭核准既有未核准的路由。
grants 與 autoApprovers 的完整語法見 ACL / Grants 入門、Tags 與 auth key,以及 Subnet Router 和 Exit Node 各自的「存取規則」一節。
DNS 與 MagicDNS
路由器預設會吃 Tailscale 的 DNS 設定(--accept-dns),而 OpenWrt 上沒有一般的 DNS 管理程式,Tailscale 會直接覆寫 /etc/resolv.conf,和負責區網 DNS 的 dnsmasq 打架。路由器自己只是閘道、不需要當 tailnet 的 DNS client,所以關掉它:
tailscale set --accept-dns=false
/etc/init.d/tailscale restart重啟讓設定立即生效(官方 wiki 也建議這一步)。想要讓區網裝置也能用 MagicDNS 名稱(*.ts.net)解析 tailnet 裡的主機,官方 wiki 的建議是把 Tailscale 的 resolver 加進 dnsmasq,而不是讓路由器吃 MagicDNS:
uci add_list dhcp.@dnsmasq[0].server='/ts.net/100.100.100.100'
uci commit dhcp
/etc/init.d/dnsmasq restart這樣區網裝置查 *.ts.net 時會轉給 100.100.100.100(MagicDNS),其他查詢照走原本的 DNS。詳細背景見官方 Why is resolv.conf being overwritten?。
小結
| 步驟 | 做什麼 |
|---|---|
apk add tailscale |
裝官方 feed 套件(25.12 為 1.102.3;套件預設用 nftables) |
| unmanaged 介面 + zone | tailscale0 專屬 zone:masq '1'、mtu_fix '1'、input/output ACCEPT |
| forwarding | subnet router 加 tailscale → lan;exit node 加 tailscale → wan |
| 廣告路由 | tailscale set --advertise-routes=... --snat-subnet-routes=false(避免雙重 NAT)或 --advertise-exit-node |
| 收尾 | 關 MagicDNS(--accept-dns=false)、打 tag、核准路由或設 autoApprovers |
最容易踩的三個坑:忘了建 firewall zone(tailnet 流量被擋)、沿襲舊教學加 --netfilter-mode=off(在 23.0.5 之後既多餘又更容易出錯)、--snat-subnet-routes=false 配 exit node(會衝突)。顧好這三個,路由器當 subnet router/exit node 就穩了。
延伸
- 路由器之外的其他平台:華碩機型走 Asuswrt-Merlin + Entware,Docker 環境則見 Docker 官方映像檔。
- subnet router 與 exit node 的完整觀念、
--accept-routes與效能調校(UDP GRO、BBR),見 Subnet Router 與 Exit Node。 - 直連還是走 DERP 中繼,影響路由器當出口時的延遲,見直連與 DERP。
參考資料
- Tailscale(OpenWrt 官方 wiki)
- Firewall configuration /etc/config/firewall(OpenWrt 官方 wiki)
- opkg 到 apk 對照表(OpenWrt 官方 wiki)
- openwrt/packages 的 tailscale 套件(openwrt-25.12 分支)
- openwrt/luci 的 luci-app-tailscale-community(openwrt-25.12 分支)
- luci-app-tailscale-community(上游,Tokisaki-Galaxy)
- Tailscale netfilter modes(官方)
- Firewall mode in tailscaled(官方)
- Subnet routers(官方)
- Exit nodes(官方)
- Why is resolv.conf being overwritten?(官方)
- Subnet Router:存取整個區網內沒裝 Tailscale 的設備
- Exit Node:讓所有流量經由指定裝置出去