站里有没有网络大佬,帮我分析下 WireGuard 是怎么做到绕开防火墙的?

网络是这样的,两台电脑分别在两个不同的环境中,A电脑上级是 OpenWrt,OpenWrt 网口1负责拨号上网,网口2连接到Wi-Fi路由器(仅仅是AP作用,有线中继),A连接这个Wi-Fi。
同样电脑B也是这样的网络拓扑。而且它们都有 OpenWrt 下发的 ipv6 地址。

我测试过,A电脑使用 server python3 -m http.server 8080 --bind :: 开一个 server,在 B 电脑使用 ipv6 地址+端口,是无法访问的。因为请求到达 A电脑上级 OpenWrt 时候,防火墙的 FORWARD 链默认是 DROP 的。 在防火墙将其转发打开,那么 B 电脑就可以正常访问了。

但是,双方电脑都使用 tailscale 组网的情况下,双方是可以通过 ipv6 直连的。通过 tailscale ping 就可以看到 pong from b (100.64.0.4) via [2408:xxxx:xxx:xxx::9c8]:35973 in 8ms

所以我不理解,tailscale(WireGuard)是怎么做到突破防火墙限制的?

WireGuard 虽然在电脑端开一个虚拟网卡和其他设备组内网,但这只是在内网环境中的。最终数据包是要经过 OpenWrt 的。比如 A 向 B 发一个数据包,经过互联网,先是到达 B 的上级 OpenWrt 的网口1,数据包的目标地址是 B 的 ipv6 地址,那么经过 OpenWrt 的路由表指示这个包要转发到网口2,于是在 OpenWrt 中网口1转发到网口2是要经过防火墙的 FORWARD 链的,默认是 DROP。但实际情况是可以正常连接,那它是怎么做到的?

19 个赞

简单一句话,这就是打洞哇。
原理我记得tailscale 博客有篇很详细的解释。

文章在这里:

1 个赞

仔细阅读这篇文章,你绝对可以弄明白 “为什么”。
:tieba_095:

谢谢你,我也好奇 :tieba_095:

关注一下,一样想了解。

但是我觉得佬给的信息不太够,最好列出 openwrt 上的 iptables FORWARD 链配置的所有规则出来。

好象两边有一边有公网IP就行,要不就再上个中转。

我按照上面的描述,我觉得问题应该是,Openwrt 路由器的 iptables FORWARD Chain 配置的是规则是 DROP,为什么还能转发到 WIFI 设备。

我觉得其实是 iptables FORWARD Chain 还配置了允许 Wireguard 的通过的规则,例如满足 UDP + Port 是 51820。

两边都在内网的话,流量会通过tailscale的服务器中转吧。。。

楼主的疑问是:

  • 内网设备发起外部连接时, iptables 会创建连接跟踪(conntrack)条目
  • 防火墙会自动允许这个连接的相关回包通过
  • 这是打洞利用的原理

[OpenWrt Wiki] 如何配置防火墙

如上图:
OpenWRT 默认丢弃任何没有匹配到已有连接的包,言下之意,是允许已经建立连接的包通过的,并非一概丢弃,这也是任何防火墙的职责所在。
也就是说 OpenWRT 默认还配置了类似:
iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT 的规则。

3 个赞

我现在更倾向于我这 OpenWrt 的问题,昨天防火墙的 FORWARD 默认是 DROP ,经过开一个 server 测试,确实是这样的。但是 tailscale 能用 v6 直连。但今天再测,就不能用 v6 直连了,而且我现在把 FORWARD 规则改成默认 ACCEPT 也不行。
由于是办公室的网络环境,不太方便 flush 掉所有规则做测试,具体是这样的:

$ ip6tables -L FORWARD -vn
Chain FORWARD (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
257K 161M forwarding_rule all * * ::/0 ::/0 /* !fw3: Custom forwarding rule chain */
226K 153M ACCEPT all * * ::/0 ::/0 ctstate RELATED,ESTABLISHED /* !fw3 */
16675 2716K zone_lan_forward all br-lan * ::/0 ::/0 /* !fw3 */
14322 5367K zone_wan_forward all pppoe-ppp * ::/0 ::/0 /* !fw3 */
0 0 zone_docker_forward all docker0 * ::/0 ::/0 /* !fw3 */
0 0 zone_ipsecserver_forward all ipsec0 * ::/0 ::/0 /* !fw3 */
0 0 reject all * * ::/0 ::/0 /* !fw3 */

# root @ OpenWrt in ~ [20:32:49]
$ iptables -L forwarding_rule -v -n
Chain forwarding_rule (1 references)
pkts bytes target prot opt in out source destination
1314K 1142M RETURN all -- pppoe+ * 0.0.0.0/0 0.0.0.0/0
886K 333M RETURN all -- * pppoe+ 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- ppp+ * 0.0.0.0/0 0.0.0.0/0 ctstate NEW
0 0 ACCEPT all -- * ppp+ 0.0.0.0/0 0.0.0.0/0 ctstate NEW

数据包是这样走的,数据包来到网口 1 后,经过 forward 链,然后第一条是自建的 forwarding_rule(我也不知道谁建的这条 rule ),这个 rule 表示,如果是从 pppoe 开头的网口,那么数据包会 RETURN ,RETURN 代表向下一条规则过滤,也就是走到 ACCEPT 这一条,这一条后面是 ctstate RELATED,ESTABLISHED,代表如果是出站过的流量,那么返回的响应是正常接收的。
然后就是下面这些 OpenWrt 自定义的规则了。能够看到数据包也小,数据量都是在 forwarding_rule 上,估计使用 tailscale 连接是被这条链捕获记录。

1 个赞

应该不是,WireGuard 是客户端装的,对于流经 OpenWrt 的数据包来讲,op 根本不知道是什么类型的数据包,它仅仅是符合 tcpip 模型的,有 IP 和 端口,ip 发现不是自己的,就转发到另一个网口。

1 个赞