Utilizing CloudFront's hidden CDN server in China

使用CloudFront隐藏的国内节点

V2Ray的白话文教程中有一段是教学如何为自己的节点使用Cloudflare CDN,这种方法建立于Cloudflare支持转发WebSocket和gRPC流量的情况下。其实有很多CDN也支持转发WebSocket流量,比如CloudFront、Gcore等等。

而CloudFront甚至有隐藏的中国节点,如果用于翻墙,那么我们的客户端根本不用处理经过GFW的流量,这部分工作都交给CloudFront了。

由于CloudFront公布的IP地址段中不包含此类节点,我们需要从全部的中国IP中扫描出来才行。

准备域名

  1. CloudFront访问源时仅支持域名,所以你需要一个指向你的服务器IP地址的域名,作为“源域”。
  2. 另外,你还需要一个域名作为访问CloudFront的“备用域名”,这个域名的IP我们可以指定,只要是CloudFront的IP就可以。

免费付费都好。我用的是 nic.eu.org 的免费域名。

准备翻墙服务器

使用Shadowsocks的Go版本v2ray-plugin,以WebSocket传输,工作在80端口,不要TLS。你需要确保$PATH中有v2ray程序以及/usr/bin/go-shadowsocks2存在。

创建/etc/systemd/system/go-shadowsocks.service:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[Unit]
Description=Shadowsocks2
Wants=network-online.target
After=network-online.target

[Service]
User=root
ExecStart=/usr/bin/go-shadowsocks2 -s 'ss://AEAD_AES_256_GCM:PASSWORD@:80' -verbose -plugin v2ray -plugin-opts "server"
Nice=-10
KillMode=process
Restart=on-failure
ProtectSystem=full
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target

请将PASSWORD替换为你想要的值。

启动服务: systemctl start go-shadowsocks

此时访问 http://源域 应当收到“Bad Request”,这是V2Ray插件的标准回复,说明你的操作没有错误。

配置CloudFront

进入CloudFront创建分配,填入之前配置的源域,协议选择“仅 HTTP”。

“缓存策略”我选择是“CachingOptimized”;“源请求策略”我选择的是“AllViewer”;”允许的 HTTP 方法”我选择的是最多的一项。

为了可以指定IP,我们还需要配置“备用域名”。输入备用域名、请求并在CloudFront应用证书等操作过程我不再描述。

配置完成后,在浏览器访问https://备用域名也应当收到“Bad Request”,这是经过CloudFront服务器中转回来的结果,这表明我们的CloudFront配置无误。

扫描IP

我使用的工具是gscan_quic,IP段来自IPIP制作中国大陆地区ip列表 每2月更新一次。配置文件 config.user.json如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"ScanWorker":1000,
"ScanMode": "SNI",
"SNI": {
"ScanCountPerIP": 1,
"ServerName": ["d1emgxxou2di91.cloudfront.net"],
"VerifyCommonName": "*.cloudfront.net",
"HTTPVerifyHosts": ["d1emgxxou2di91.cloudfront.net"],
"HandshakeTimeout": 800,
"ValidStatusCode": 400,
"ScanMinRTT": 0,
"ScanMaxRTT": 1000,
"OutputSeparator": "\n",
"InputFile": "./iprange_china_ip_list.txt",
"OutputFile": "./out_cloudfront_cn.txt",
"Level": 2
}
}

  1. 其中ScanWorker是扫描的线程数,如果你用这个值扫描时速度非常快,那么它其实根本没有逐个扫描,你该考虑降低这个值,到最大的合适位置为止。
  2. ServerName是连接时使用的SNI值,建议将其替换为你的CloudFront域名。
  3. VerifyCommonName是验证证书上的CommonName。
  4. HTTPVerifyHosts是发起HTTP请求时使用的Host
  5. ValidStatusCode时验证HTTP回复时要确认的状态码。
  6. Level使用1时仅确认TCP连通性、2是除了1的验证之外还要验证证书的CommonName、3是除了1和2的验证之外还要验证对HTTP请求的回复的状态码。

我放在VPS上扫描,速度为 1000 IP/s ,中国IP总共有354835264个,用时大概98.56小时。

处理扫描结果

由于远程VPS的网络环境和本机不同,如果您也使用的是VPS扫描,建议在本地使用相同的配置再对前面的扫描结果扫描一遍,以排除在VPS可用,而在本地不可用的IP。

完成这一步之后,请看下文,关于如何排除特定结果。

有一些IP向其443端口发起HTTPS请求时返回的CommomName为*.iot.irobot.cn,这样的IP建议排除,因为根据本人经验,这样的用一段时间之后其443端口就不能对其正常发起TLS请求,过一段时间会恢复。这种情况在一些博文中被称为“断流”。目前并不清楚原因为何,但是有一些可能性:

  1. 防火长城阻断: GFW看到对于这些端口的连接之后做出记录,连接数目达到一定值时干扰其连接。这种可能性较低,因为假设GFW存在于国际网络出口的话,路由追踪显示对这些中国IP的连接根本不涉及国外节点,即根本不过GFW。
  2. 服务器速率限制。连接多了会触发服务器的速率限制,禁止之后的连接。虽然我不懂为什么限制方式是禁止发起TLS请求。

排除方法

在gscan_quic里扫描时ServerName设为8964.iot.irobot.cnVerifyCommonName设为*.iot.irobot.cn,扫描后在vim中:sort结果,将其和同样sort过的之前的结果用diff命令比较,即可得出不返回该CommonName的IP地址。

优选IP

我使用的工具是CloudflareSpeedTest

ip.txt填入我们上面处理好的IP,然后执行命令: ./CloudflareST -t 1 -url 测速地址

关于测速地址,请观看“参考资料”中bulianglin.com发布的视频。

优选后的IP输出在result.csv文件。

应用IP地址

去你的DNS管理界面将备用域名指向刚才优选过的IP即可,可以多指定几个。

使用

我使用的是Clash,节点示例配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- name: example
type: ss
server: 备用域名
servername: 备用域名
port: 443
cipher: aes-256-gcm
password: PASSWORD
udp: true
plugin: v2ray-plugin
mode: websocket
tls: true
host: 备用域名
path: "/"
mux: true

请自行替换各值。

参考资料

黑话

  • 棉被: 免备案
  • 北岸: 备案