控制家长的家长控制:GFW@Home

dark_mode format_list_bulleted
控制家长的家长控制:GFW@Home
男儿当自墙:我真得好好控制你了
history
History
censor
networking
visibility public group

啊我操短视频怎么这么坏!我真想口了他们!👍👍👍🌹🌹🌹

家里人是不是经常给向你转发玄学垃圾、民粹暴论?甚至「脱口而脱出」那既不新鲜也不美味的谬误,一库一库,噗叽啪,很响啊!

带着计网基础打算成为赛博老大哥,于是~你的目标是:

  • 家中的老人:阻断内容推送「精准诈骗」,阻断「智人 TV」相关的转发对你的智识污染。之后也许你会考虑让他们转用其他媒体。
  • 电丸の小鬼:「小鬼,我要控制你了」
  • 小🍥:肯定不吃你这一套,然后把这个给她看!

设备一旦离开了受审查的网络,就脱离了控制;若能在设备上进行主动审查,确实是最万无一失的方案。 如果设备支持设置 Private DNS,将其设置为你搭设的 DNS over TLS 服务器,也可以对 DNS 请求进行过滤,但是对于由应用程序发起的加密查询,这不起作用。 也许可以给他们装一个 CA 证书用于中间人攻击,这样可以让他们的设备在任何网络上都无法脱身,但是这样做要接触和调试物理设备。 对于没有解 OEM Bootloader 锁的设备,只能通过 VPN 连接对设备流量代理与解密。

本篇不含有以下元素:

  • DNS 抢答
  • 深度包检测
  • 主动探测

旁路方案:用于过滤和欺骗的 DNS

[!TIP]

与「DNS 抢答」不同,本文的方式会直接对下游设备指定要使用的 DNS 的地址。

用什么 DNS 服务器套件?

AdGuard Home,为过滤而生,带 Web 配置界面,附带 DHCPv4 服务器,可以代替网关路由器的 DHCPv4 服务器。

该怎么向设备下发 DNS 地址?

DNS 地址自动配置需要借助 DHCPv4Router Advertisement 下发到设备。

与 IPv4 不同:DHCPv6 并不是 IPv6 常用的网络自动配置方案,而是 SLAAC 和 NDP RA。 因此你需要在网关路由器上配置 RA 报文的内容,而不是 DHCPv6,后者无所谓。

DHCPv4

  • 如果你的网关路由器可以指定 DNS 地址,那填入你搭设的 DNS 的地址。
  • 如果没有,禁用你网关路由器的 DHCPv4 服务器,然后搭建一个网内唯一的 DHCPv4 服务器,配置其下发的 DNS 地址指向你的 DNS 服务器。

IPv6 NDP RA

由 RA 下发的 DNS 地址,只能在网关路由器上配置,可以是 fe80:: 这样的本地链路地址。

对于被过滤的查询,应该返回怎样的响应?

视情况,二选一:

  • 拒绝,使握手立刻失败:
    • NXDOMAIN
    • 空地址「::」「0.0.0.0
  • 丢包,使握手永无响应:返回一个拥有 「可路由前缀」 然而 「不可达」 的地址,比如 「192.168.254.254
||dns.google$dnsrewrite=0.0.0.0
||dns.google$dnsrewrite=192.168.254.254

更进一步:在路径上串联中间设备

桥接

为了监视数据包,需要在数据包路径中加入一个中间设备,用多于 2 个网口的机器,组一个桥接网络,其中一个网口接上游,其余的网口接下游,使数据包流经该桥接网络,以使之对软件可见。

见「附录」。

启用 netfilter 桥接数据包转发

需要 br_netfilter 内核模块。

桥接网络中的 IP 包将对 iptables 可见。

modprobe br_netfilter

#sysctl net.bridge.bridge-nf-call-ip6tables=1
#sysctl net.ipv6.conf.br0.forwarding=1
sysctl net.ipv4.ip_forward=1

br_netfilter 已知问题

IPv6 桥接数据包转发(启用 net.bridge.bridge-nf-call-ip6tables 时)下游设备没有收到 RA 报文,导致下游设备没有 IPv6 地址用。

捕获模式下的 WireShark

  • 监听物理网口,不要监听桥接
  • 从入口到出口的包经过了 iptables 的过滤,出入口的流中的包是有差别的

核心科技:「和谐」才有共处

使用 OpenGFW 和 iptables 对目标进行 TCP 连接重置。

对于使用 DNS over HTTPS,或大陆服务商自营的与 DoH 类似的 HTTP DNS 变种,污染无法触及查询。

但是大陆封锁了 Cloudflare ECH,事实上反对 Encrypted Client-Hello,因此对于大陆服务商与用户而言 SNI 的暴露是必然的,所以识别 SNI 并屏蔽连接总是可行的,无论是 TCP/TLS 还是 QUIC 握手包中的 SNI 都在裸奔。

国之重器:OpenGFW

OpenGFW 可以解析多种数据包中的字段,包括但不限于:

  • TCP/TLS
  • QUIC

还有

  • HTTP
  • HTTPS
  • SOCKS
  • Xray 等隧道代理

配置 OpenGFW 本身

# /etc/opengfw.yaml
io:
  queueSize: 1024
  queueNum: 100
  rcvBuf: 4194304
  sndBuf: 4194304
  # 监视 FORWARD 数据包
  local: false
  # 对被认定应阻止的连接进行重置
  rst: true

workers:
  count: 4
  queueSize: 64
  tcpMaxBufferedPagesTotal: 65536
  tcpMaxBufferedPagesPerConn: 16
  tcpTimeout: 10m
  udpMaxStreams: 4096

配置 OpenGFW 规则

# /etc/opengfw-rules.yaml
- name: Reset TCP/TLS
  action: block
  expr: |
    let sni = string(tls?.req?.sni);
    sni contains "www.baidu" ||
    sni contains "mi.com" ||
    sni contains "sogou" ||
    sni contains "so.com" ||
    sni contains "search.we" ||
    sni contains "telemetry"

- name: Block QUIC
  action: block
  expr: string(quic?.req?.sni) endsWith "taobao.com"
opengfw -c /etc/opengfw.yaml /etc/opengfw-rules.yaml

社会信用:iptables

如果只是针对 TCP,OpenGFW 反而不是一个比 iptables 更简单的选择。

对于像 SNI 这种,明文中包含特定内容的,我们可以将「他们」列入暂时管制名单:不许你上网! 可以对付像「微信」这种使用 HTTP DNS 的服务。

对于在「你目前的配置」下总是漏网的连接,可以采取:对于 TCP,每捕获一次特征

  • 必需:持续 60s 重置到目标主机的连接,通常只会影响客户端到目标 CDN 的连接,够用
  • 可选:持续 2s 重置来自源主机的连接,直接断网,属于过正矫枉,会破坏网络整体观感

阻断时长根据情况调整,如果其他服务与视频服务不共用同一个 CDN,把时间拉长到 36000s 也行。

还可以把 --rcheck 替换为 --update,只要源主机不再发起符合特征的连接,对应的网络服务就会在设定时长后恢复,否则计时器会被重置为设定时长。

# 这些 xt_recent 规则用于记录地址并在设定的时间内持续阻断
iptables -A FORWARD -p tcp -m recent --name bad_src --rsource --rcheck --second 2  -j REJECT --reject-with tcp-reset
iptables -A FORWARD -p tcp -m recent --name bad_dst --rdest   --rcheck --second 60 -j REJECT --reject-with tcp-reset
# 匹配数据包明文中的子串,比如 SNI
for K in `cat /etc/sni.list | con`; do
  iptables -A FORWARD -p tcp -m string --algo bm --string "$K" -m recent --name bad_src --rsource --set -j REJECT --reject-with tcp-reset
  iptables -A FORWARD -p tcp -m string --algo bm --string "$K" -m recent --name bad_dst --rdest   --set -j REJECT --reject-with tcp-reset
done

下游路由器

如果还搭设了下游路由器用作受审设备的网络入口,那可以为其建一个 chain 专门处理它:

iptables -N gfw

# 假定下游路由器的地址是 192.168.0.10
iptables -A FORWARD -p tcp -s 192.168.0.10 -j gfw

iptables -A gfw -p tcp -m recent --name bad_src --rsource --rcheck --second 2  -j REJECT --reject-with tcp-reset
iptables -A gfw -p tcp -m recent --name bad_dst --rdest   --rcheck --second 60 -j REJECT --reject-with tcp-reset

for K in `cat /etc/sni.list | con`; do
  iptables -A gfw -p tcp -m string --algo bm --string "$K" -m recent --name bad_src --rsource --set -j REJECT --reject-with tcp-reset
  iptables -A gfw -p tcp -m string --algo bm --string "$K" -m recent --name bad_dst --rdest   --set -j REJECT --reject-with tcp-reset
done

其他考虑

华为路由器

我在「v2ex」「恩山无线」等论坛上了解到「华为」有路由器型号具有屏蔽「微信视频号」的选项,具体实现方法不知。 想省事的可以考虑。

QUIC

此时应用 OpenGFW 来解析 QUIC 握手包字段。

QUIC 在大陆还没有完全普及,运营商对 UDP 的 QoS 可能会比较低,大陆大部分服务商还在用 TCP,暂时不需要针对 QUIC 连接;而且 QUIC 需要更多计算来解密握手包,审查成本会更高。 至于 UDP 承载的服务相当多,考虑到还有不少专有协议,阻断会比较麻烦。

br_netfilter 转发 IPv6 开启后,反而致使下游收不到 RA 报文

我不知道如何解决;如果下游有 IPv6 硬需求的话,自寻出路吧。

可能面临的现实困境

[!TIP]

阻断只能得一时安宁,还得对家人做后期工作。

呼叫装维人员上门

可能会将你的中间设备从路径中移除,或者将你的光猫设置重置。

目标迁至其他网络

只能对设备物理干预。

绿坝行为:在设备上进行限制,比如「青少年模式」之类的应用锁。

另寻其他短视频平台

这个比较难办,如果执意要阻断这些短视频平台,可以从下游的 DNS 查询历史记录中寻找目标。 魔高一尺,道高一丈。

建议为家里人寻找其他兴趣活动,或者转移到质量较好的长媒体,哪怕付点订阅费呢。

暴露后如何解释?

这个真就艺术吧,谁知道呢,最好别发生。

观察记录

依赖明文 DNS 的服务

例如:

  • 搜索引擎,比如「百度」「搜狗」
  • 字节跳动/抖音/火山/西瓜/头条
  • 快手

对查询返回欺骗结果即可。

比如,如果你想迫使下游使用 Microsoft Bing,可以把其他的搜索引擎全部封掉,然后在设备上装 Firefox,把 Bing 设为默认。

微信视频号

播放「微信视频号」时,在重置「微信」的全部连接后,用 tcpdump 观察到了很多明文的 JSON,看上去是控制媒体播放的反馈信息,但是尚不明确对连接是否有影响。

「微信视频号」比较难杀,有漏网的连接,可采用上文中「根据特征识别、阻断目标地址」的手段。

规则列表

DNS 污染

通过污染下游的 DNS 记录,可使依赖明文 DNS 查询的下游客户端与域名对应的服务失联。

除此之外,一并在 AdGuard Home 的面板上开启对「TikTok」等服务的过滤。

# AdGuard Home 过滤规则

/.*douyin.*/
/.*ixigua.*/
/.*huoshan.*/
/.*tiktok.*/
||bytedance.com^
||snssdk.com^
||bytecdn.cn^
||bdurl.net^
||yximgs.com^
||ksapisrv.com^
||inkuai.com^
||kwimgs.com^
||gifshow.com^
||ndcimgs.com^
||ks-live.com^
/.*kuai.*/
/.*kwai.*/
||ximalaya.com^
||himalaya.com^
||xmcdn.com^
||netstart.cn^

明文特征

结合 DNS 查询历史记录和 WireShark 抓包寻找服务的特征,将 SNI 加入到 iptables 过滤规则中。

# 把明文 JSON 作为特征
":{

# 微信视频号
flv.wxqcloud
video.qq
snsvideo
stodownload
voipfinder

愿智人 TV 不会来打扰你和家里人(大概?)

附录

适用于 systemd-networkd 的桥接网络配置示例

# 桥接设备
# /etc/systemd/network/br0.netdev

[NetDev]
Name=br0
Kind=bridge
MACAddress=fe:fe:fe:fe:fe:fe

# 桥接网络
# /etc/systemd/network/br0.network

[Match]
Name=br0

[Link]
RequiredForOnline=routable

# 如果通过 DHCP 获取地址:
[Network]
DHCP=yes
# 如果你的设备正好是网络中仅有的 DHCPv4 服务器,那你需要为设备指定静态地址:
[Network]
Address=192.168.1.2/24
Gateway=192.168.1.1
Gateway=fe80::1
DNS=192.168.1.1
DNS=2606:4700:4700::1111

# 网口桥接
# /etc/systemd/network/en.network

[Match]
# 通配符,假设有多个 Ethernet 网口 enp1s0 enp2s0 enp3s0
Name=en*

[Network]
Bridge=br0
Remark