v2ray反向代理实战

用v2ray搭个反向代理,留个痕迹方便以后抄作业,就不折腾frp了。

(v2ray的反代还是太烂了,frp真香)

入门:微软远程桌面反代#

teamviewer太辣鸡了。
teamviewer太辣鸡了。
teamviewer太辣鸡了。

重要的事情说三遍,现在还不如win10系统自带的远程桌面,至少没辣么恶心。

需要反代的机子#

这机子就是指你要控制的那台电脑,与一般的远程连接相似,只不过流量是走的反代而已。

这例子的情景需要在控制的那台电脑上运行v2ray,对应的config.json

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
{
"reverse": {
"bridges": [
{
"tag": "remote_desktop_bridge_01",
"domain": "rd-bridge-01.zhouxuebin.club"
}
]
},
"outbounds": [
{
"tag": "tunnel",
"protocol": "vmess",
"settings": {
"domainStrategy": "UseIPv4",
"vnext": [
{
"address": "xxx.xxx.xxx.xxx",
"port": 2021,
"users": [
{
"id": "00112233-4455-6677-8899-aabbccddeeff",
"alterId": 1,
"security": "auto"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"tcpSettings": {}
}
},
{
"tag": "bridge_out",
"protocol": "freedom",
"settings": {}
}
],
"routing": {
"rules": [
{
"type": "field",
"inboundTag": ["remote_desktop_bridge_01"],
"domain": ["full:rd-bridge-01.zhouxuebin.club"],
"outboundTag": "tunnel"
},
{
"type": "field",
"inboundTag": ["remote_desktop_bridge_01"],
"outboundTag": "bridge_out"
}
]
}
}

bridges的domain是随便取的,只要运行反代的服务器能够区分开就好,不需要真实存在。而outbounds有两个,一个走的是本地协议的,也就是bridge_out

这台机子承载了两个方向的流量,一个是正向流量,另一个是反向流量,v2ray靠的是domain字段队两个方向的流量进行区分的。

正向流量是指:VPS主机(xxx.xxx.xxx.xxx:2021) -> 反代机子(inbound tag:remote_desktop_bridge_01)-> v2ray路由(outbound tag:bridge_out) -> 本地协议(127.127.1.1:3389
反向流量是指:本地协议的响应数据 -> 反代机子(inbound tag:remote_desktop_bridge_01,domain:rd-bridge-01.zhouxuebin.club) -> v2ray路由(outbound tag:tunnel) -> VPS主机

如果嘴瓢说反了,那也没关系,问题不大的。

VPS#

VPS负责接受两个方向的连接,以及中转两个方向的流量,对应的config.json

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
{
"reverse": {
"portals": [
{
"tag": "remote_desktop_portal_01",
"domain": "rd-bridge-01.zhouxuebin.club"
}
]
},
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"port": 2021,
"listen": "0.0.0.0",
"tag": "bridge_in",
"protocol": "vmess",
"settings": {
"clients": [
{
"id": "00112233-4455-6677-8899-aabbccddeeff",
"level": 0,
"alterId": 1,
}
]
},
"streamSettings": {
"network": "tcp"
}
}
],
"outbounds": [
{
"protocol": "freedom",
"settings": {},
"tag": "direct"
},
{
"protocol": "blackhole",
"settings": {},
"tag": "blocked"
}
],
"routing": {
"domainStrategy": "IPOnDemand",
"rules": [
{
"type": "field",
"inboundTag": ["bridge_in"],
"outboundTag": "remote_desktop_portal_01"
}
]
}
}

VPS的工作其实也挺简单的,就一个inbound负责监听和建立反代机子和访问反代的机子两者的连接,实现流量交换,甚至不需要outboundrouting就只要把inbound流量转发到portal就好了。这里重点放在reverse设置上,portals里面的domain必须与反代主机上填的domain一致,无论这个domain是否实际存在。如果需要区分反代机子和访问反代机子的访问,可以在inboundrouting里面调整。

直接通过VPS的IP访问反代机子的远程桌面,在inbound增加个任意门协议负责监听3389端口,把流量转发到portal就行了。这样访问反代(远程桌面)的机子可以直接通过VPS的IP或者域名进行连接,就不需要本地再跑v2ray了。例子见入门2,这个例子就直接在VPS监听。

访问反代的机子#

本例子这里还需要在访问的机子上再运行一个v2ray,才能把数据发往VPS,config.json如下:

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
{
"inbounds": [
{
"protocol": "dokodemo-door",
"listen": "127.127.1.1",
"port": 3389,
"settings": {
"address": "127.127.1.1",
"port": 3389,
"protocol": "tcp"
}
}
],
"outbounds": [
{
"protocol": "vmess",
"settings": {
"domainStrategy": "UseIPv4",
"vnext": [
{
"address": "xxx.xxx.xxx.xxx",
"port": 2021,
"users": [
{
"id": "00112233-4455-6677-8899-aabbccddeeff",
"alterId": 1,
"security": "auto"
}
]
}
]
},
"streamSettings": {
"network": "tcp"
}
}
]
}

具体来说,就是负责把访问本地127.127.1.1:3389(这里是listen的IP)的流量封装到反代的机子上,按照反代机子的本地协议转发到127.127.1.1:3389(这里是settings里面的address)。这样说可能比较抽象,用另外一个简单的例子说明:

1
2
3
4
5
6
7
8
9
10
{
"protocol": "dokodemo-door",
"listen": "127.0.0.1",
"port": 8088,
"settings": {
"address": "192.168.1.101",
"port": 80,
"protocol": "tcp"
}
}

这样你每次在本地访问127.0.0.1:8088,实际上流量转发到反代机子后,反代机子通过本地的网络连接到192.168.1.101:80,然后把结果再通过反代传回本地。

示意图#

懒,有空再画

入门2:ssh反代#

这个例子讲述了用任意门协议直接在VPS上监听反代连接,这样就不需要在访问反代的电脑上运行v2ray了。

注意:实际上这么做是非常危险的,直接把ssh访问暴露在公网非常容易收到攻击。假如该机子的root密码是简单的123456,那么其他人就可以轻易地登上服务器并运行rm -rf /。如果确定要这么做,建议把密码登录关了,只留密钥登录。

需要反代的服务器#

需要通过反代建立ssh连接的服务器的配置:

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
{
"reverse": {
"bridges": [
{
"tag": "ssh_rev_br_01",
"domain": "ssh-rev-01.zhouxuebin.club"
}
]
},
"log": {
"access": "",
"error": "",
"loglevel": "warning"
},
"inbounds": [
],
"outbounds": [
{
"tag": "tunnel",
"protocol": "vmess",
"settings": {
"domainStrategy": "UseIPv4",
"vnext": [
{
"address": "xxx.xxx.xxx.xxx",
"port": 2021,
"users": [
{
"id": "00112233-4455-6677-8899-aabbccddeeff",
"alterId": 1,
"security": "auto"
}
]
}
]
},
"streamSettings": {
"network": "tcp"
}
},
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {}
}
],
"routing": {
"rules": [
{
"type": "field",
"inboundTag": ["ssh_rev_br_01"],
"domain": ["ssh-rev-01.zhouxuebin.club"],
"outboundTag": "tunnel"
},
{
"type": "field",
"inboundTag": ["ssh_rev_br_01"],
"outboundTag": "direct"
}
]
}
}

跟入门1相同,一个bridge,两个outbound(direct和tunnel),两条routing规则。bridges的domain依然是不必存在的,outbound的address填的是自己VPS的IP或域名。

VPS#

VPS上的配置文件:

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
{
"reverse": {
"portals": [
{
"tag": "ssh_rev_portal01",
"domain": "ssh-rev-01.zhouxuebin.club"
}
]
},
"log": {
"access": "",
"error": "",
"loglevel": "warning"
},
"inbounds": [
{
"port": 2021,
"listen": "0.0.0.0",
"tag": "bridge_in",
"protocol": "vmess",
"settings": {
"clients": [
{
"id": "00112233-4455-6677-8899-aabbccddeeff",
"alterId": 1,
"level": 0,
"email": "ssh01@user.com"
}
]
},
"streamSettings": {
"network": "tcp"
}
},
{
"protocol": "dokodemo-door",
"tag": "ssh_in",
"listen": "0.0.0.0",
"port": 4001,
"settings": {
"address": "127.0.0.1",
"port": 22,
"protocol": "tcp"
}
}
],
"outbounds": [],
"routing": {
"rules": [
{
"type": "field",
"inboundTag": ["bridge_in"],
"outboundTag": "ssh_rev_portal01"
},
{
"type": "field",
"inboundTag": ["ssh_in"],
"outboundTag": "ssh_rev_portal01"
}
]
}
}

使用方式:ssh username@vps.domain -p 4001,这样就相当于在反代的机子上运行ssh username@127.0.0.1 -p 22

进阶:代理与反代流量的混合#

这里就放出inboundsrouting就算了,其他大同小异。代理和反代的流量跑的是本地的websocket,通过apache加TLS层封装为wss再对外开放。为了不占用根路径,这里的path改成了/ray,也就是伪装成wss://xxxx/ray协议,这么麻烦还是因为apache同时承担着网页服务器部分的流量……

inbounds:这里的client_traffic_mix同时承载了反向代理和普通代理的流量。

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
{
"inbounds": [
{
"tag": "client_traffic_mix",
"listen": "127.0.0.1",
"port": 2019,
"protocol": "vmess",
"settings": {
"clients": [
{
"id": "00112233-4455-6677-8899-aabbccddeeff",
"alterId": 1,
"level": 0,
"email": "whatever@it.is"
}
]
},
"streamSettings": {
"network": "ws",
"wsSettings": {
"path": "/ray"
}
},
"sniffing": {
"enabled": "true",
"destOverride": [
"http",
"tls"
]
}
}
]
}

自己的id和alterId就填clients里面。

routing:负责区分两个代理的流量,说实话好像也没什么比较好的解决方案。现在采用的是分IP的方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"routing": {
"domainStrategy": "IPOnDemand",
"rules": [
{
"type": "field",
"inboundTag": ["client_traffic_mix"],
"ip": [
"127.127.1.1"
],
"outboundTag": "remote_desktop_portal_01"
},
{
"type": "field",
"inboundTag": ["client_traffic_mix"],
"outboundTag": "direct"
}
]
}
}

这里根据访问的IP进行流量区分,也就是说访问127.127.1.1的流量转发到反代的tag,从反代的机子出,其他流量都直接从VPS出。

还有一种是分用户的方案,其实也大同小异,routing部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"routing": {
"domainStrategy": "IPOnDemand",
"rules": [
{
"type": "field",
"user": ["whatever@it.is"],
"outboundTag": "remote_desktop_portal_01"
},
{
"type": "field",
"inboundTag": ["client_traffic_mix"],
"outboundTag": "direct"
}
]
}
}

这样就直接把emailwhatever@it.is用户的所有流量转发到反代的portal了,其他的就直接走outbound tag为direct的代理流量。

附一个apache2转发外部wss/https到本地ws/http流量:

1
2
3
4
5
6
7
8
9
10
11
<IfModule mod_ssl.c>
<VirtualHost _default_:443>
# ......
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /ray(.*) ws://localhost:2019/ray$1 [P,L]
SSLProxyEngine On
ProxyPass /ray http://localhost:2019
ProxyPassReverse /ray http://localhost:2019
</VirtualHost>
</IfModule>