Skip to content

OpenWrt 裝 Tailscale:防火牆 zone、subnet router 與 exit node

在 OpenWrt 路由器上安裝官方 feed 的 tailscale 與 luci-app-tailscale-community 套件,建立 tailscale0 專用防火牆 zone(masquerade 與 MSS clamping 的 UCI 寫法),並說明 --snat-subnet-routes=false 的意義與當 subnet router、exit node 的完整設定和最小權限 grants

Updated View as Markdown

把 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 內有多個介面時才有作用;這裡的 tailscale zone 只涵蓋 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 reload

forwarding 是單向的,要雙向就各寫一條(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 up

OpenWrt 的核心預設已開啟 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 就穩了。

延伸

參考資料

Navigation

Type to search…

↑↓ navigate↵ selectEsc close