在 ImmortalWrt 上配置 Tailscale 并开放入站访问

Tailscale 基于 WireGuard 协议构建,可为不同位置的设备组建安全的私有虚拟局域网。本文将介绍如何在 ImmortalWrt(OpenWRT 分支,使用 fw4 / nftables 防火墙)上安装、配置 Tailscale,并通过防火墙规则开放来自 Tailscale 网络的入站访问,使远程设备能够访问路由器的管理页面及其他服务。

前置条件

  • 运行 ImmortalWrt 的路由器,具备 root shell 访问权限
  • 客户端设备已安装 Tailscale(手机 / 电脑均可)
  • 路由器可通过软件源正常安装软件包

第一步:安装 Tailscale

ImmortalWrt 软件源中内置了 Tailscale 软件包,直接安装即可:

opkg update
opkg install tailscale

INFO

安装完成后,Tailscale 将注册为系统服务,但默认不会开机自启,需要手动启用:

/etc/init.d/tailscaled enable
/etc/init.d/tailscaled start

第二步:登录并加入 Tailscale 网络

执行 tailscale up 登录。本文配置了 --accept-dns=false,使路由器不接管内网设备的 DNS 解析:

tailscale up --accept-dns=false

终端会输出一条登录链接,在浏览器中打开并授权该设备即可。登录完成后确认节点状态:

tailscale status
tailscale ip -4

节点应显示 online,并分配到一个 100.64.0.0/10 网段的地址,例如:

root@ImmortalWrt:~# tailscale ip -4
100.106.45.156

WARNING

此时从客户端设备访问该地址,通常会失败。原因在于 ImmortalWrt 的默认防火墙策略拒绝了来自 Tailscale 接口的入站流量,需要在第三步中配置放行规则。

第三步:配置防火墙入站放行

防火墙 zone 的局限性

ImmortalWrt 默认的防火墙策略为 input=REJECT,即拒绝所有发往路由器本机的入站连接。要允许来自 Tailscale 网络的访问,需要先放行入站流量。

一种常见的做法是为 tailscale0 接口创建防火墙 zone:

uci add firewall zone
uci set firewall.@zone[-1].name='tailscale'
uci set firewall.@zone[-1].network='tailscale0'
uci set firewall.@zone[-1].input='ACCEPT'
uci commit firewall
/etc/init.d/firewall restart

然而该方案无法生效。检查 fw4 print 的输出可以发现,zone 的设备集合为空:

define tailscale_devices = {  }
define tailscale_subnets = {  }

zone 的 network 选项引用的是 /etc/config/network 中定义的逻辑接口。tailscale0 是由 tailscaled 自行创建的虚拟网卡,并未在 netifd 中注册,因此 zone 无法匹配到任何设备,放行规则不会生效,流量仍被默认策略拒绝。

INFO

可通过 uci get firewall.@defaults[0].input 确认默认策略为 REJECTuci show firewall | grep -i tailscale 无输出则表明 tailscale0 未被任何 zone 接纳。

配置入站放行规则

绕过 zone 机制,直接创建一条防火墙规则放行整个 Tailscale CGNAT 网段(100.64.0.0/10),并通过 device 选项限定只匹配 tailscale0 接口:

uci add firewall rule
uci set firewall.@rule[-1].name='Allow-Tailnet-Input'
uci set firewall.@rule[-1].family='ipv4'
uci set firewall.@rule[-1].src_ip='100.64.0.0/10'
uci set firewall.@rule[-1].proto='all'
uci set firewall.@rule[-1].target='ACCEPT'
uci set firewall.@rule[-1].src='*'
uci set firewall.@rule[-1].device='tailscale0'
uci commit firewall
/etc/init.d/firewall restart

WARNING

src='*'device='tailscale0' 两个选项缺一不可:

  • src='*':fw4 会将未指定 src/dest 的规则默认放入 output 链,而源地址匹配 saddr 100.64.0.0/10 在 output 链中不会命中任何入站流量。指定 src='*' 后规则才会落入全局 input 链,且位于 jump handle_reject 之前。
  • device='tailscale0':将规则限定为仅匹配从 Tailscale 接口进入的流量,避免对 WAN 口造成任何放行。

若执行上述命令时出现 uci: Invalid argument,通常是 section 引用错误所致。uci add firewall rule 创建的匿名 section(如 cfg1092bd)与具名索引(如 @rule[9])引用方式不同,可先执行 uci show firewall | grep -i tailnet 确认实际 section 编号。

验证配置

检查规则是否已正确生成于 input 链:

fw4 print | grep -B6 -A2 'Allow-Tailnet-Input'

规则应出现在 chain input 中、位于 jump handle_reject 之前:

chain input {
    type filter hook input priority filter; policy drop;
    iif "lo" accept
    ct state vmap { established : accept, related : accept }
    tcp flags & (fin | syn | rst | ack) == syn jump syn_flood
    iifname "tailscale0" ip saddr 100.64.0.0/10 counter accept comment "!fw4: Allow-Tailnet-Input"
    iifname "br-lan" jump input_lan
    jump handle_reject
}

随后从客户端设备访问路由器:

curl http://100.106.45.156/

返回 HTTP 200 即表示配置成功。

工作原理

配置完成后,来自 Tailscale 网络的流量路径如下:

  1. 客户端将数据包经 Tailscale 隧道发送至路由器的 tailscale0 接口
  2. 数据包进入 nftables 的 chain input(hook input,priority filter)
  3. 命中 iifname "tailscale0" ip saddr 100.64.0.0/10 规则,直接 accept
  4. uhttpd / OpenClash 等服务处理请求并返回响应
  5. 回程流量由 conntrack 状态机制(ct state established,related accept)放行

WAN 口的入站流量不匹配该规则,仍会被链尾的 jump handle_reject 拒绝,安全性不受影响。

INFO

Tailscale 的 NetfilterMode 默认为 2(nftables)。在此模式下,tailscaled 不会自行向系统防火墙添加规则,入站放行完全依赖 fw4 配置,因此必须手动完成上述设置。

常见问题排查

节点在线,但 tailscale ping 通、普通 ping 不通

tailscale ping 使用 Tailscale 自身的 Disco / 加密握手协议,不经过系统防火墙的 input 链;而普通 ICMP / TCP 流量会真实进入网卡,被防火墙拒绝。若返回「目标端口不可达」而非超时,表明流量被防火墙主动拒绝而非丢弃,说明放行规则未生效或未正确匹配。

停止 OpenClash 后仍无法访问

OpenClash 停止后可能残留策略路由或防火墙规则(例如将流量劫持至已失效的 utun 接口),可通过以下命令检查:

ip rule show
ip route show table all | grep -E '100\.'
nft list ruleset | grep -iE 'clash|openclash|utun' | head -30

若存在残留规则,重启 OpenClash 使其重新初始化,或手动清理相关规则。

使用全局 input=ACCEPT 进行定位

firewall.@defaults[0].input 临时改为 ACCEPT,可快速判断问题是否出在 fw4 的 input 链。测试完成后务必改回 REJECT,全局 ACCEPT 会使 WAN 口入站全部开放,存在安全隐患。

安全注意事项

  • 本方案放行整个 Tailscale 网段(100.64.0.0/10)到路由器本机的所有入站流量。Tailscale 网络本身为私有网络,仅授权设备可接入,风险可控;如需进一步收紧,可为规则添加 proto tcpdest_port 限制。
  • 本方案仅开放路由器本机服务(管理页面、代理面板等)。如需通过 Tailscale 访问局域网内的其他设备,需先在 /etc/config/network 中将 tailscale0 注册为 tun 设备(config device + option type 'tun'),再配置 zone 转发与 masq,fw4 的 zone 方可正常匹配。
  • 客户端设备上运行本地代理(如 Clash 类软件)时,可能拦截 100.64.0.0/10 网段流量,测试时应为该网段添加直连规则或暂时关闭代理。

添加新评论