Featured image of post 我的家庭网络设计思路,开启debian的旁路由之路(五)

我的家庭网络设计思路,开启debian的旁路由之路(五)

本篇主要讲解UnblockNeteaseMusic的安装和配置,涉及到的nftables规则,以及在客户端上如何使用。

前言

我们继续趁热打铁,将UnblockNeteaseMusic也一起部署了,因为它也要用到 nftables 来实现。

下载与安装

unblockneteasemusic 这个名称太长了,我们把它更名为 unblocknem。我默认你已经按照 前文(二) 安装好了必要的软件了。以下为root用户运行的命令。

1
2
3
4
5
6
7
8
9
## 检查最新稳定版的版本号,如果获取不到请检查网络
remote_ver=$(curl -sS https://api.github.com/repos/UnblockNeteaseMusic/server/releases/latest | jq -r .tag_name | sed 's|v||' | grep -v "null"); echo $remote_ver

## 下载最新稳定版(前一句有输出这一句才能正常执行)
cd /tmp
wget -q --progress=bar:dot --show-progress -O "unblockneteasemusic-linux-x64" "https://github.com/UnblockNeteaseMusic/server/releases/download/v${remote_ver}/unblockneteasemusic-linux-x64"

## 安装
install -p unblockneteasemusic-linux-x64 /usr/local/bin/unblocknem

创建服务

unblocknem.service

创建工作目录/var/lib/unblocknem

1
mkdir -p /var/lib/unblocknem

创建文件 /etc/systemd/system/unblocknem.service,内容如下。这时 clash 的配置文件为 /var/lib/unblocknem/unblocknem.env

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
[Unit]
Description = Revive unavailable songs for Netease Cloud Music (Refactored & Enhanced version).
Wants       = network-online.target clash.service
After       = network-online.target

[Service]
Type               = simple
Restart            = always
EnvironmentFile    = -/var/lib/unblocknem/unblocknem.env
WorkingDirectory   = /var/lib/unblocknem
ExecStart          = /usr/local/bin/unblocknem
RestartSec         = 10
StartLimitInterval = 0

[Install]
WantedBy = multi-user.target

如果你比较讲究Linux哲学,可以把 EnvironmentFile 改成下面这样,这时 unblocknem 的配置文件为 /etc/default/unblocknem

1
EnvironmentFile    = -/etc/default/unblocknem

如果不想以root用户运行 unblocknem ,可以自行创建用户 unblocknem 和用户组 unblocknem ,然后在 /etc/systemd/system/unblocknem.service[Service] 单元下增加下面两行,同时修改 /var/lib/unblocknem 及其子文件为该普通用户所有。(后续的脚本都是以root用户为运行用户进行的,如果要把 unblocknem 服务调整为普通用户运行,请自行修改脚本)。

1
2
User  = unblocknem
Group = unblocknem

注意:由于各音源政策一直在变化,可能unblockneteasemusic默认的音源顺序(见官方README)无法使用,你可以查看unblocknem.service的运行日志来确认(方式见后文),这时需要你自己调整音源的顺序,也就是在ExecStart中增加-o参数,同时设置/etc/default/unblocknem中的FOLLOW_SOURCE_ORDER=trueExecStart设置音源顺序举例如下:

1
ExecStart = /usr/local/bin/unblocknem -o ytdlp bilibili

安装依赖

前文(二) 我们已经安装了UnblockNeteaseMusic的一个依赖项: yt-dlp,在实际运行中,部分情况下还需要另外一个依赖:phantomjs。这个软件已经多年没有更新过了。

1
2
3
4
cd /tmp
wget -q --progress=bar:dot --show-progress -O phantomjs-2.1.1-linux-x86_64.tar.bz2 https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2
tar --no-same-owner -xf phantomjs-2.1.1-linux-x86_64.tar.bz2  # 需要安装bzip2才能解压
install -p phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin/phantomjs

unblocknem 的配置文件

没有配置文件, unblocknem 也是可以以默认配置启动的。如有需要,可以新建 /var/lib/unblocknem/unblocknem.env/etc/default/unblocknem,内容如下,请注意按照注释修改。全部环境变量详见 官方README

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# 全部环境变量详见:https://github.com/UnblockNeteaseMusic/server

# 激活无损音质获取
ENABLE_FLAC=true

# 激活本地黑胶 VIP,可选值:true(等同于 CVIP)、cvip 和 svip
ENABLE_LOCAL_VIP=svip

# 仅对这些 UID 激活本地黑胶 VIP,默认为对全部用户生效
# LOCAL_VIP_UID=123456789,1234,123456

# 激活故障的 Netease HTTPDNS 查询(不建议)
# ENABLE_HTTPDNS=true

# 屏蔽应用内部分广告
BLOCK_ADS=true

# 禁用更新检测
DISABLE_UPGRADE_CHECK=true

# 激活开发模式。需要自己用 yarn 安装依赖 (dependencies)
# DEVELOPMENT=true

# 严格按照配置音源的顺序进行查询
# FOLLOW_SOURCE_ORDER=true

# 输出机器可读的 JSON 记录格式
# JSON_LOG=true

# 停用 cache
# NO_CACHE=true

# 允许的最低源音质,小于该值将被替换
# MIN_BR=320000

# 选择所有音源中的最高码率替换音频
SELECT_MAX_BR=true

# 日志输出等级。请见〈日志等级〉部分
# LOG_LEVEL=debug

# 从 Pino 端设置日志输出的文件位置。也可以用 *sh 的输出重导向功能 (node app.js >> app.log) 代替
# LOG_FILE=app.log

# JOOX 音源的 wmid 和 session_key cookie
# JOOX_COOKIE="wmid=<your_wmid>; session_key=<your_session_key>"

# 咪咕音源的 aversionid cookie
# MIGU_COOKIE="<your_aversionid>"

# QQ 音源的 uin 和 qm_keyst cookie
# QQ_COOKIE="uin=123456789; qm_keyst=Q_H_L_6hTegBdoCzFj6bcUUpuhkOP11234ChsOkfKiOdvcIOFKiQQf4oGjykQ"

# Youtube 音源的 Data API v3 Key
# YOUTUBE_KEY="Adjeuskn12xdEvQfLHnvfC12386QAApcOt5Kh1g"

# 自定义证书文件
SIGN_CERT="/var/lib/unblocknem/server.crt"

# 自定义密钥文件
SIGN_KEY="/var/lib/unblocknem/server.key"

# 在其他音源搜索歌曲时携带专辑名称(默认搜索条件 歌曲名 - 歌手,启用后搜索条件 歌曲名 - 歌手 专辑名
# SEARCH_ALBUM=true

生成自己的证书

1
2
3
cd /var/lib/unblocknem
wget https://raw.githubusercontent.com/UnblockNeteaseMusic/server/enhanced/generate-cert.sh
env TYPE="RSA" ISSUENAME="Your Name" bash generate-cert.sh

脚本会在 /var/lib/unblocknem 目录下生成这些文件。如果你不以root用户来启动 unblocknem,则请注意修改它们的所有者。

1
2
3
4
5
6
7
/var/lib/unblocknem
├── ca.crt
├── ca.key
├── ca.srl
├── server.crt
├── server.csr
└── server.key

调整 nftables 规则

前文(四) 中我们为 clash 创建了一些 nftables 规则,现在我们多了 unblocknem,它也需要,所以我们要调整一下 前文(四) 中的 /usr/local/bin/mynftables.nft,调整为下面这个样子。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
#!/usr/sbin/nft -f

## 清空旧规则
flush ruleset

## 只处理指定网卡的流量,要和ip规则中的接口操持一致
define interface = ens18

## clash的透明代理端口
define tproxy_port = 7895

## clash打的标记(routing-mark)
define clash_mark = 666

## 常规流量标记,ip rule中加的标记,要和ip规则中保持一致,对应 "ip rule add fwmark 1 lookup 100" 中的 "1"
define default_mark = 1

## 本机运行了服务并且需要在公网上访问的tcp端口(本机开放在公网上的端口),仅本地局域网访问的服务端口可不用在此变量中,以半角逗号分隔
define local_tcp_port = {
    2222,      # ssh,按需设置
    8080-8088  # nginx webui,按需设置
}

## 要绕过的局域网内tcp流量经由本机访问的目标端口,也就是允许局域网内其他主机主动设置DNS服务器为其他服务器,而非旁路由
define lan_2_dport_tcp = {
    53     # dns查询
}

## 要绕过的局域网内udp流量经由本机访问的目标端口,也就是允许局域网内其他主机主动设置DNS服务器为其他服务器,而非旁路由;另外也允许局域网内其他主机访问远程的NTP服务器
define lan_2_dport_udp = {
    53,    # dns查询
    123    # ntp端口
}

## 保留ip地址
define private_address = {
    127.0.0.0/8,
    100.64.0.0/10,
    169.254.0.0/16,
    224.0.0.0/4,
    240.0.0.0/4,
    10.0.0.0/8,
    172.16.0.0/12,
    192.168.0.0/16
}

## 大陆ip地址
include "/var/lib/clash/geoip4_cn.nft"

## 网易云音乐ip地址
include "/var/lib/clash/nem_address.nft"

table ip clash {

    ## 保留ipv4集合
    set private_address_set {
        type ipv4_addr
        flags interval
        elements = $private_address
    }

    ## 网易云音乐ipv4集合
    set nem_address_set {
        type ipv4_addr
        flags interval
        elements = $nem_address
    }

    ## 大陆ipv4集合
    set geoip4_cn_set {
        type ipv4_addr
        flags interval
        elements = $geoip4_cn
    }

    ## prerouting链
    chain prerouting {
        type filter hook prerouting priority filter; policy accept;
        ip protocol { tcp, udp } socket transparent 1 meta mark set $default_mark accept # 绕过已经建立的连接
        meta mark $default_mark goto clash_tproxy                                        # 已经打上default_mark标记的属于本机流量转过来的,直接进入透明代理
        fib daddr type { local, broadcast, anycast, multicast } accept                   # 绕过本地、单播、组播、多播地址
        tcp dport $lan_2_dport_tcp accept                                                # 绕过经由本机到目标端口的tcp流量
        udp dport $lan_2_dport_udp accept                                                # 绕过经由本地到目标端口的udp流量
        ip daddr @private_address_set accept                                             # 绕过目标地址为保留ip的地址
        ip daddr @nem_address_set goto clash_tproxy                                      # 将网易云音乐的流量转发到透明代理
        ip daddr @geoip4_cn_set accept                                                   # 绕过目标地址为大陆ip的地址
        # ip protocol udp accept                                                         # 绕过全部udp流量(udp不进行透明代理)
        goto clash_tproxy                                                                # 其他流量透明代理到clash
    }

    ## 透明代理
    chain clash_tproxy {
        ip protocol { tcp, udp } tproxy to :$tproxy_port meta mark set $default_mark
    }

    ## output链
    chain output {
        type route hook output priority filter; policy accept;
        oifname != $interface accept                                   # 绕过本机内部通信的流量(接口lo)
        meta mark $clash_mark accept                                   # 绕过本机clash发出的流量
        fib daddr type { local, broadcast, anycast, multicast } accept # 绕过本地、单播、组播、多播地址
        udp dport { 53, 123 } accept                                   # 绕过本机dns查询、NTP流量
        tcp sport $local_tcp_port accept                               # 绕过本地运行了服务的tcp端口,如果并不需要从公网访问这些端口,可以注释掉本行
        ip daddr @private_address_set accept                           # 绕过目标地址为保留ip的地址
        ip daddr @geoip4_cn_set accept                                 # 绕过目标地址为大陆ip的地址
        ip protocol { tcp, udp } meta mark set $default_mark           # 其他流量重路由到prerouting
    }
}

注意到我们在第51行多引用了一个文件 /var/lib/clash/nem_address.nft (可先下载这个并非最新的文件到指定位置来将就一下:nem_address.nft,我们将在 本系列第(七)篇 用定时任务来自动更新这个文件),在第63-67行多形成了一个集合 nem_address_set,在第85行插入了一行规则 ip daddr @nem_address_set goto clash_tproxy,这条规则插入在 ip daddr @geoip4_cn_set accept 之前,表示虽然网易云音乐IP是中国大陆IP,但要在直接接收前转到透明代理并停止后续规则。现在的 prerouting 链内容有点多,其实可以一拆为三。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    ## prerouting链
    chain prerouting {
        type filter hook prerouting priority filter; policy accept;
        fib daddr type { local, broadcast, anycast, multicast } accept # 绕过本地、单播、组播、多播地址
        tcp dport $lan_2_dport_tcp accept                              # 绕过经由本机到目标端口的tcp流量
        udp dport $lan_2_dport_udp accept                              # 绕过经由本地到目标端口的udp流量
        ip daddr @private_address_set accept                           # 绕过目标地址为保留ip的地址
        ip daddr @geoip4_cn_set accept                                 # 绕过目标地址为大陆ip的地址
        # ip protocol udp accept                                       # 绕过全部udp流量(udp不进行透明代理)
        goto clash_tproxy                                              # 其他流量透明代理到clash
    }

    ## prerouting链绕过已经建立的连接,已经打上default_mark标记的属于本机流量转过来的,直接进入透明代理
    chain divert {
        type filter hook prerouting priority mangle; policy accept;
        ip protocol { tcp, udp } socket transparent 1 meta mark set $default_mark accept
        meta mark $default_mark goto clash_tproxy
    }

    ## 局域网中访问网易云音乐的流量,第一次全部进行透明代理,优先级需要比绕过大陆ip先执行(mangle为-150,filter为0)
    chain neteasemusic {
        type filter hook prerouting priority dstnat; policy accept;
        ip daddr @nem_address_set goto clash_tproxy
    }

一拆为三后,新的 divert 链的优先级为 mangle (-150),新的neteasemusic 链的优先级为 dstnat (-100),而新的 prerouting 链则仍是 filter (0),数值越小,优先级越高,所以 ip daddr @nem_address_set goto clash_tproxy 这条规则仍然早于 ip daddr @geoip4_cn_set accept 执行。这样也可以让网易云音乐IP在被直接接收前就转到透明代理去。

unblocknem 工作在应用层,clash 转给它后,它出来的流量会经由 output 链中的 ip daddr @geoip4_cn_set accept 直接出去,不会形成环路(output 链并没有再劫持这部分流量)。这也就意味着上述规则只能代理局域网中除旁路由以外其他设备(以旁路由为网关的)的网易云音乐流量,不能代理旁路由自己的网易云音乐流量。

设想一个这样的场景:unblocknem 设置了 ytdlp(Youtube)为劫持音源,那么局域网中以旁路由为网关的客户端A在播放灰色歌曲触发 unblocknem 时,流量是怎么运作的呢?

对照前文(四)的题图,客户端A的流量先进入旁路由的 prerouting 链,按上述说明触发该链中的 ip daddr @nem_address_set goto clash_tproxy 规则将流量转向我们的 unblocknem HTTP代理,然后 unblocknem 代理会将流量劫持向Youtube,也就是说 unblocknem 会在旁路由中产生向Youtube的请求。前文已经说过,unblocknem 工作在应用层,它产生的流量会先进入 output 链。如果它请求的是国内的音源,那么将会由 output 链中 ip daddr @geoip4_cn_set accept 规则直接放行,而现在它请求的是Youtube需要走代理,所以流量会在 output 链中触发 ip protocol { tcp, udp } meta mark set $default_mark 这条规则,将其打上标记并重路由到 prerouting 链,然后按照前文(四)所说进行透明代理。也就是说,在这个假设场景下,客户端A在播放灰色歌曲时,流量经过了clash的两次透明代理。

调整 clash 配置文件

我们后续将在 本系列第(七)篇 用定时任务来自动在 clash 的配置中自动插入下面这些内容。

clash 的配置文件中 proxies: 部分新增:

1
2
3
4
  - name: 🎶 网易代理
    server: 10.0.0.2
    port: 8080
    type: http

proxy-groups: 部分新增:

1
2
3
4
5
  - name: 🎶 网易音乐
    type: select
    proxies:
      - 🎶 网易代理
      - DIRECT

rules: 部分新增(各地IP可能是不一样的,我们将在 本系列第(七)篇 用定时任务来自动生成它,将其刷新为你本地的解析IP):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  - DOMAIN-SUFFIX,music.163.com,🎶 网易音乐
  - DOMAIN-SUFFIX,music.163.com.163jiasu.com,🎶 网易音乐
  - IP-CIDR,39.105.63.80/32,🎶 网易音乐,no-resolve
  - IP-CIDR,39.105.175.128/32,🎶 网易音乐,no-resolve
  - IP-CIDR,45.127.129.53/32,🎶 网易音乐,no-resolve
  - IP-CIDR,47.100.127.239/32,🎶 网易音乐,no-resolve
  - IP-CIDR,59.111.19.33/32,🎶 网易音乐,no-resolve
  - IP-CIDR,59.111.19.99/32,🎶 网易音乐,no-resolve
  - IP-CIDR,59.111.160.195/32,🎶 网易音乐,no-resolve
  - IP-CIDR,112.47.51.30/32,🎶 网易音乐,no-resolve
  - IP-CIDR,114.80.149.72/30,🎶 网易音乐,no-resolve
  - IP-CIDR,115.238.119.67/32,🎶 网易音乐,no-resolve
  - IP-CIDR,115.238.119.68/31,🎶 网易音乐,no-resolve
  - IP-CIDR,118.24.63.156/32,🎶 网易音乐,no-resolve
  - IP-CIDR,121.228.190.68/30,🎶 网易音乐,no-resolve
  - IP-CIDR,122.225.83.106/32,🎶 网易音乐,no-resolve
  - IP-CIDR,122.225.83.109/32,🎶 网易音乐,no-resolve
  - IP-CIDR,122.225.83.110/32,🎶 网易音乐,no-resolve
  - IP-CIDR,180.163.200.96/30,🎶 网易音乐,no-resolve
  - IP-CIDR,182.92.170.253/32,🎶 网易音乐,no-resolve
  - IP-CIDR,183.134.34.248/31,🎶 网易音乐,no-resolve
  - IP-CIDR,183.136.182.18/31,🎶 网易音乐,no-resolve
  - IP-CIDR,183.136.182.20/32,🎶 网易音乐,no-resolve
  - IP-CIDR,193.112.159.225/32,🎶 网易音乐,no-resolve

网易云音乐IP只允许解析IPv4

因为我们的透明代理只代理 IPv4 流量,所以我们不能让网易云音乐客户端解析出 IPv6 地址。有好几种方式可以实现,既可以在 mosdns 中实现,也可以在 adguardhome 中实现,但后者有 webui 要方便些,并且 adguardhome 是第一级,它有可能会切换到后备服务器而不向 mosdns 发起请求,所以我们就选后者吧。

adguardhome 中实现也有两种方式,其一是将 “设置 -> DNS设置 -> 上游DNS服务器” 设置为下面这样(这种方式不会计入“被拦截域名”中,但是如若 adguardhomeclash 遇到故障会切换到后备,对后备是不生效的),直接将网易云音乐的解析发到 clash 去解析,因为我们关闭了 clash 的DNS模块的 IPv6 解析。

1
2
127.0.0.1:5335
[/music.163.com/music.ntes53.netease.com/music.163.com.163jiasu.com/music.163.com.bsclink.cn/uz99.v.bsclink.cn/]127.0.0.1:7874

其二是在 “过滤器 -> 自定义过滤规则” 中添加规则如下(这种方式会计入“被拦截域名”中,但是对包括后备在内的全部上游DNS服务器都有效)。

1
2
3
4
5
6
7
||music.163.com^$dnstype=AAAA
||music.ntes53.netease.com^$dnstype=AAAA
||music.163.com.163jiasu.com^$dnstype=AAAA
||music.163.com.bsclink.cn^$dnstype=AAAA
||music.163.com.c.vedcdnlb.com^$dnstype=AAAA
||s.bdsa.cdnbuild.net^$dnstype=AAAA
||uz99.v.bsclink.cn^$dnstype=AAAA

只能保证目前有效。上述两种方式,都由于 CNAME 域名的存在,光处理 music.163.commusic.163.com.163jiasu.com 不行,还要处理它们背后的 CNAME 域名。可能有望在未来得以改进,详见 Hosts-Blocklists#dnstype

启用服务

完全配置好以后,我们可以设置 /etc/systemd/system/unblocknem.service 为开启自动启动,并立即启动起来。如果这个时候 clash 还没有启动起来,那么 unblocknem 服务就还不可用。都启动好后,unblocknem 将会监听在 80808081 端口上。

1
systemctl enable --now unblocknem.service

后续如想查看日志,我们直接使用Debian自带的工具来查看:

1
journalctl -efu unblocknem.service

如果想要重启:

1
systemctl restart unblocknem.service

客户端

安卓系统不支持。

Windows

安装网易云音乐客户端后,创建一个快捷方式,然后在该快捷方式的属性中的“目标”增加 --ignore-certificate-errors,注意有个空格。

网易云音乐

ArchLinux系

将刚刚生成的 /var/lib/unblocknem/ca.crt 下载下来,然后运行:

1
sudo trust anchor --store ca.crt

在启动命令行后面添加 --ignore-certificate-errors,然后启动即可。

Debian系

将刚刚生成的 /var/lib/unblocknem/ca.crt 下载下来,放在 /usr/local/share/ca-certificates 下,然后运行:

1
sudo update-ca-certificates

在启动命令行后面添加 --ignore-certificate-errors,然后启动即可。

最后

打开 clashwebuihttp://10.0.0.2:9090/ui/xd/#/proxies ,选择 网易音乐网易代理;你再打开网易云音乐客户端后,在 http://10.0.0.2:9090/ui/xd/#/conns 就能看到许多连接都接管到 UnblockNeteaseMusic 了。

clash将网易云音乐的流量透明代理到了unblocknem

unblocknem.service 的日志中也能看到它的作用,这些灰色歌曲都被接管了。

unblocknem的日志

系列

Built with Hugo
主题 StackJimmy 设计