0%

记一次绕过币安API地区限制的折腾经历

请在符合国家法律法规的前提下食用本文!

币安被美国罚款43亿美元!CEO辞职!
虽然币安很早以前就禁止美国用户注册,但是现在币安的API也开始限制美国IP了,导致我的机器人从宣布罚款第二天开始就无法正常使用。直到今天,我终于忍无可忍,决定想办法绕过这个限制。

环境介绍

简单描述一下我的机器人:

1
用户 -> Discord -> 托管在Cloudflare Worker上的Discord Bot -> 币安API

换句话说,无论用户来自哪里,币安收到的API请求都是通过Cloudflare Worker发出的。而由于Discord服务器主要位于美国,因此到达Cloudflare Edge的地域也几乎都是美国,所以币安理所应当认为这些请求都是来自美国的。

所以很简单,我需要找到一个办法,可以让Cloudflare Worker发出的请求看起来像是来自其他国家的。

解决方案1

经过一通搜索,成功找到了这篇文章和作者的演示视频

虽然其原理我依然有点一知半解,但经过一整天的测试,终于成功复现了作者说明的方法。以下是步骤:

假如我们有一个Cloudflare Worker,承担核心的API功能,名字是demoworker,代码如下:

1
2
3
4
5
export default {
async fetch(request, env, ctx) {
return new Response('Hello World!');
},
};

同时,我们有一个域名,假如是logiconsole.com

创建一个代理Worker

首先,我们需要创建一个代理Worker,就叫demoworkerproxy好了。代码如下:

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
async function proxy(request) {
var headers = request.headers;
return fetch('https://demoworker.logiconsole.com', {
body: request.body,
headers: headers,
method: request.method,
redirect: 'manual',
cf: {
resolveOverride: 'demoworker.logiconsole.com'
},
});
}

async function forceRegion(request) {
return fetch(request.url, {
headers: request.headers,
body: request.body,
method: request.method,
redirect: 'manual',
cf: {
resolveOverride: 'twdemoworkerproxy.logiconsole.com'
},
});
}

addEventListener('fetch', event => {
return event.respondWith(
['US', 'CN'].includes(String(event.request.cf.country)) ?
forceRegion(event.request)
:
proxy(event.request)
);
})

先不用管上面代码的含义,后面会解释。但记住logiconsole.com是我自己的域名,到时候要改成你的域名。

创建Worker Routes

然后,我们需要创建两个Worker Routes,分别指向上面的两个Worker,如下图所示:

注意把域名改成自己的域名。

创建DNS A记录

接下来,我们需要创建两个DNS A记录,分别指向两个Worker Routes。这里要注意,通常我们会直接在Worker的Triggers中添加Custom Domain,让Cloudflare自动帮我们创建类型是Worker的DNS记录,但是这里我们需要手动创建A记录,并且将IP地址设置为任意一个Cloudflare Warp的IP地址,如下图所示:

图中的188.114.96.3就是一个Cloudflare Warp的IP地址,你也可以自己去找其他能用的IP。可以看到,我还添加了一个twdemoworkerproxy的记录,对应的IP地址8.39.126.5同样是一个Cloudflare Warp的IP地址,但是位于台湾。

解释

当有一个请求访问demoworkerproxy.logiconsole.com时,由于该域名被指向了Cloudflare的网络,且设置了Worker Routes,所以它被指向了demoworkerproxy这个Worker。通过这段代码:

1
2
3
4
5
6
7
8
addEventListener('fetch', event => {
return event.respondWith(
['US', 'CN'].includes(String(event.request.cf.country)) ?
forceRegion(event.request)
:
proxy(event.request)
);
})

Worker会先判断请求的来源地区,如果是美国或者中国,就会走forceRegion方法。(把中国也加进来是方便本地测试)

1
2
3
4
5
6
7
8
9
10
11
async function forceRegion(request) {
return fetch(request.url, {
headers: request.headers,
body: request.body,
method: request.method,
redirect: 'manual',
cf: {
resolveOverride: 'twdemoworkerproxy.logiconsole.com'
},
});
}

forceRegion的方法简单来说,就是在请求中添加cf:{resolveOverride: 'twdemoworkerproxy.logiconsole.com'},再原封不动向自己发送一遍请求。由于resolveOverride的存在,Cloudflare会去解析twdemoworkerproxy.logiconsole.com这个域名,而这个域名指向的是Cloudflare在台湾的机房IP,且同样通过*demoworkerproxy.logiconsole.com/*这个Worker Route指向了demoworkerproxy。所以Cloudflare会通过台湾的机房再请求一次demoworkerproxy。又因为twdemoworkerproxy.logiconsole.comdemoworkerproxy.logiconsole.com处于同一个域名下,所以请求中的所有信息都会被透明地传递下来。

接下来,我们打开Worker的日志,并且访问demoworkerproxy.logiconsole.com,可以看到如下日志:

其中较早的信息如下:

很明显是我本地的IP地址,而较晚的信息如下:

注意headers中的访问来源信息已经不是中国了(但居然是英国,在我之前的测试中,应该统一都是台湾的信息。可能是IP信息乱了),而’colo’这个值变成了TPE,即台北机场的代码。

既然访问来源已经不是US或者CN了,那么就不会再走forceRegion方法,而是走proxy方法,也就是直接向demoworker发送请求,即正常访问原本的API了。

查找不同地区Cloudflare的IP段

首先,可以通过这个仓库下载最新的geolite.mmdb,其中的GeoLite2-ASN.mmdb可以查找IP段对应的ASN,GeoLite2-Country.mmdb可以查找IP段对应的国家。只要找到ASN为13335、组织是CLOUDFLARENET的IP段,就是Cloudflare的IP段了。接下来和国家的IP段互相对应,就能找到不同地区的Cloudflare IP段了。
另外,Linux下可以通过nmap -n -sP {IP段}这个命令扫描该IP段下所有可用的IP地址。

万万没想到

然而用了这个方法之后,我发现我的机器人依然无法正常工作。经过反复测试验证后,我才不得不承认,币安貌似是把Cloudflare所有的IP段都给封了……

解决方案2

既然方案1行不通,那就只能来简单粗暴的了:找个机房,写一个简单的代理脚本,把所有的请求都转发到该机房,再由该机房转发到币安。这样,币安就会认为所有的请求都是来自该机房了。
没什么好说的,直接上仓库:
https://github.com/dale0525/binance-server.git

经过反复衡量采用了Docker方案,方便在不同机房迁移。在Docker前面加一个反向代理即可,如果实在偷懒,直接访问IP:端口也是可以的。

另外,推荐一个开源的运维管理面板:1Panel
比宝塔透明,并且至少SSL证书续签从来没遇到问题。

总结

本文其实主要是记录一下Cloudflare Worker强制指定机房的方法,毕竟方案2没什么好说的。