前言:前段时间新出现了几个支持IKEv2的VPN,试用了一下,感受速度嗖嗖的,不过流量贵贵的。作为一个有尊严的技术人员,怎能甘心就这样把钱轻易送给别人呢。经过一番观察,发现该vpn直连的其实是国内的阿里云。那么原理就很明确了,客户端连阿里云,然后阿里云进行国内外分流。因为阿里云机房的网络肯定杠杠的,所以不管访问国内和国外都很快。明确原理之后,我做了好一番尝试,失败了好几次之后,终于于前天调试好了。
架构:客户端 <-> 阿里云 <-> 海外VPS
本文的重点是分流转发部分(也是我搜索许久,发现资料很少的部分),所以其它内容只是简略介绍。
零、基础知识
要跨越长城,其实得分两步:一是获取封锁网站的正确DNS解析,二是使用代理访问被封锁的IP。
就本文来说,获取正确DNS解析是通过dnsmasq
和ss
搭配来完成的。默认上游DNS是114.114.114.114
,被封锁网站走ss-tunnel
转发到8.8.8.8
解析。
更多基础知识,请阅读FreeRouter,作者从网络基础到长城运行原理及如何跨越都讲到了。
一、海外服务器搭建ss
这一步网上教程一大片,就不重复了。我用的是shadowsocks-libev。如果这一步都无法独立完成,那后边的就不需要看了。
二、国内VPS搭建strongswan
centos 直接yum install strongswan
就好。其它系统请自行搜索相应安装方式。
配置strongswan。我懒得弄证书了,所以认证方式为预共享密钥(因为我只需要iphone和mac上用,所以只配了这俩,其他客户端请自行搜索)
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
|
vi /etc/strongswan/ipsec.conf (建议先配置一下默认配置)
config setup
uniqueids=no #允许多个客户端使用同一个证书,多设备同时在线
strictcrlpolicy=no
#所有项目共用的配置项
conn %default
keyexchange=ike #ikev1 或 ikev2 都用这个
left=%any #服务器端标识,%any表示任意
leftsubnet=0.0.0.0/0 #服务器端虚拟ip, 0.0.0.0/0表示通配.
right=%any #客户端标识,%any表示任意
ikelifetime=60m
keylife=20m
rightsourceip=172.0.0.0/24 #因为阿里云默认有两个网卡,内网网卡用了10网段。所以我这里设置的是172.0.0.0/24
#供 os x 使用, 使用 PSK 预设密钥
conn IPSec-IKEv1-PSK
keyexchange=ikev1
fragmentation=yes
leftauth=psk
rightauth=psk
rightauth2=xauth
auto=add
# ios IKEv2-PSK
conn iOS_IKEv2-PSK
auto=add
dadaction=clear
keyexchange=ikev2
ike=aes256-sha1-modp1024
leftauth=psk
leftid=v.sosonemo.me
rightauth=eap-mschapv2
|
以上配置文件仅限于对我来说可用,具体各条目啥含义我也不是完全明白。
配置密码文件:
1
2
3
4
|
vi /etc/strongswan/ipsec.secrets
: PSK "xxx" #预共享密钥
user : EAP "xxx" # 用户与密码
|
配置strongswan:
1
2
3
4
5
6
7
8
9
10
11
|
vi /etc/strongswan/strongswan.conf
charon {
load_modular = yes
plugins {
include strongswan.d/charon/*.conf
}
duplicheck.enable = no #是为了你能同时连接多个设备,所以要把冗余检查关闭
compress = yes
dns1 = xxx.xxx.xxx.xxx #后文用dnsmasq,这里要设置成阿里云的公网ip。不知道为啥不能设置成127.0.0.1 如果有知道请告知
}
|
三、客户端设置
iOS9现在虽然可以选择新建IKEv2的VPN了,但是无法使用预共享密钥,所以这里需要使用mobileconfig文件。
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
|
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>IKEv2</key>
<dict>
<key>AuthName</key>
<string>{username}</string>
<key>AuthPassword</key>
<string>{password}</string>
<key>AuthenticationMethod</key>
<string>SharedSecret</string>
<key>ChildSecurityAssociationParameters</key>
<dict>
<key>DiffieHellmanGroup</key>
<integer>2</integer>
<key>EncryptionAlgorithm</key>
<string>AES</string>
<key>IntegrityAlgorithm</key>
<string>SHA1-96</string>
<key>LifeTimeInMinutes</key>
<integer>1440</integer>
</dict>
<key>DeadPeerDetectionRate</key>
<string>Medium</string>
<key>DisableMOBIKE</key>
<integer>0</integer>
<key>DisableRedirect</key>
<integer>0</integer>
<key>EnableCertificateRevocationCheck</key>
<integer>0</integer>
<key>EnablePFS</key>
<integer>0</integer>
<key>ExtendedAuthEnabled</key>
<true/>
<key>IKESecurityAssociationParameters</key>
<dict>
<key>DiffieHellmanGroup</key>
<integer>2</integer>
<key>EncryptionAlgorithm</key>
<string>AES</string>
<key>IntegrityAlgorithm</key>
<string>SHA1-96</string>
<key>LifeTimeInMinutes</key>
<integer>1440</integer>
</dict>
<key>LocalIdentifier</key>
<string>{rightid}</string>
<key>RemoteAddress</key>
<string>{your_server_address}</string>
<key>RemoteIdentifier</key>
<string>{leftid}</string>
<key>SharedSecret</key>
<string>{your_psk}</string>
<key>UseConfigurationAttributeInternalIPSubnet</key>
<integer>0</integer>
</dict>
<key>IPv4</key>
<dict>
<key>OverridePrimary</key>
<integer>1</integer>
</dict>
<key>PayloadDescription</key>
<string>Configures VPN settings</string>
<key>PayloadDisplayName</key>
<string>VPN</string>
<key>PayloadIdentifier</key>
<string>com.apple.vpn.managed.FBFBDEF8-5B16-4863-91C1-7E2A68F848A3</string>
<key>PayloadType</key>
<string>com.apple.vpn.managed</string>
<key>PayloadUUID</key>
<string>425A1628-E99B-4547-966E-5B967CF1F5EA</string>
<key>PayloadVersion</key>
<real>1</real>
<key>Proxies</key>
<dict>
<key>HTTPEnable</key>
<integer>0</integer>
<key>HTTPSEnable</key>
<integer>0</integer>
</dict>
<key>UserDefinedName</key>
<string>JP</string>
<key>VPNType</key>
<string>IKEv2</string>
<key>VendorConfig</key>
<dict/>
</dict>
</array>
<key>PayloadDisplayName</key>
<string>IKEv2</string>
<key>PayloadIdentifier</key>
<string>C7918ABA-8DE8-40ED-A3AE-994CD40ACE22</string>
<key>PayloadRemovalDisallowed</key>
<false/>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadUUID</key>
<string>9697F3C2-FF20-4981-A0C4-AA36BA78EEEA</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</plist>
|
把需要填写的参数填入,保存为.mobileconfig文件,放到任何能用safari访问到的地方用safari打开安装就好。
Mac新建VPN选择Cisco IPsec方式就行。
四、配置防火墙
1
2
3
4
5
|
iptables -A INPUT -p udp --dport 500 -j ACCEPT
iptables -A INPUT -p udp --dport 4500 -j ACCEPT #这是strongswan用到的两个端口
echo 1 > /proc/sys/net/ipv4/ip_forward #打开网卡转发功能。要把客户端的请求转发到公网网卡
iptables -t nat -A POSTROUTING -s 172.0.0.0/24 -o eth0 -j MASQUERADE #转发客户端请求。注意网段和ipsec.conf里要一致。网卡使用外网网卡,阿里云的话是eth1
iptables -A FORWARD -s 172.0.0.0/24 -j ACCEPT #允许转发
|
到此时,可以用客户端连到服务器试试能否正常连接。
五、配置中转
中转有两种方案:一种是国内ip直连,国外走代理。另一种是默认直连,黑名单走代理。这里选择前一种,因为对我个人来说,有些国外网站代理比直连要快。
服务器打开ss: nohup ss-server -u -A -c /etc/shadowsocks-libev/config.json &
国内VPS:
nohup ss-redir -c /etc/shadowsocks-libev/config.json -u -A -l 1080 -b 0.0.0.0 &
nohup ss-tunnel -c /etc/shadowsocks-libev/config.json -l 5353 -L 8.8.8.8:53 -u -A &
-u 表示打开udp转发。-A表示OTA认证,安全性更高
防火墙配置参数此脚本:(原理是下载国内ip段,生成ipset列表,访问国内ip时直连,其它ip转到ss-redir的端口)
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
|
#!/bin/sh
#自动翻墙脚本,配合shadowsocks-libev的ss-redir使用。需要ipset(sudo apt-get install ipset)
server_IP=123.45.67.89
[ -r chnroute.txt ] || curl 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest' | grep ipv4 | grep CN | awk -F\| '{ printf("%s/%d\n", $4, 32-log($5)/log(2)) }' > chnroute.txt
sudo iptables -t nat -N SHADOWSOCKS
sudo iptables -t nat -A SHADOWSOCKS -d $server_IP -j RETURN
# 内网网段
sudo iptables -t nat -A SHADOWSOCKS -d 0.0.0.0/8 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 10.0.0.0/8 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 127.0.0.0/8 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 169.254.0.0/16 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 172.16.0.0/12 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 192.168.0.0/16 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 224.0.0.0/4 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 240.0.0.0/4 -j RETURN
# 创建ipset列表
sudo ipset create chnroute hash:net
cat chnroute.txt | sudo xargs -I ip ipset add chnroute ip
sudo iptables -t nat -A SHADOWSOCKS -m set --match-set chnroute dst -j RETURN # 国内ip表直连
sudo iptables -t nat -A SHADOWSOCKS -p tcp -j REDIRECT --to-ports 1080 #其他连接走ss转发
sudo iptables -t nat -A OUTPUT -p tcp -j SHADOWSOCKS #所以请求经过SHADOWSOCKS规则处理 # 这条命令对于我来说没用,我使用的是下边的命令 #有知道为啥无效的朋友请告知
iptables -t nat -A PREROUTING -s 172.0.0.0/24 -p tcp -j SHADOWSOCKS # 172xx为vpn客户端的网段
iptables -t nat -A PREROUTING -s 172.0.0.0/24 -p udp -j SHADOWSOCKS
|
六、配置dnsmasq
centos可以yum直接安装,其它系统请自行搜索
配置dnsmasq:
把 /etc/dnsmasq.conf
最后一行conf-dir=/etc/dnsmasq.d
取消注释
新建/etc/dnsmasq.d
目录。里边新建两个文件default.conf
和gfwlist.conf
default.conf
里只需要一句话: server=114.114.114.114
表示默认上游DNS
gfwlist.conf
初次可以用下边的脚本生成。脚本来自这里。注意端口和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
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
|
import urllib2
import re
import os
import datetime
import base64
import shutil
mydnsip = '127.0.0.1'
mydnsport = '5353'
# the url of gfwlist
baseurl = 'https://autoproxy-gfwlist.googlecode.com/svn/trunk/gfwlist.txt'
# match comments/title/whitelist/ip address
comment_pattern = '^\!|\[|^@@|^\d+\.\d+\.\d+\.\d+'
domain_pattern = '([\w\-\_]+\.[\w\.\-\_]+)[\/\*]*'
tmpfile = '/tmp/gfwlisttmp'
# do not write to router internal flash directly
outfile = '/tmp/gfwlist.conf'
rulesfile = '/etc/dnsmasq.d/gfwlist.conf'
fs = file(outfile, 'w')
fs.write('# gfw list ipset rules for dnsmasq\n')
fs.write('# updated on ' + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + '\n')
fs.write('#\n')
print 'fetching list...'
content = urllib2.urlopen(baseurl, timeout=15).read().decode('base64')
# write the decoded content to file then read line by line
tfs = open(tmpfile, 'w')
tfs.write(content)
tfs.close()
tfs = open(tmpfile, 'r')
print 'page content fetched, analysis...'
# remember all blocked domains, in case of duplicate records
domainlist = []
for line in tfs.readlines():
if re.findall(comment_pattern, line):
print 'this is a comment line: ' + line
#fs.write('#' + line)
else:
domain = re.findall(domain_pattern, line)
if domain:
try:
found = domainlist.index(domain[0])
print domain[0] + ' exists.'
except ValueError:
print 'saving ' + domain[0]
domainlist.append(domain[0])
fs.write('server=/.%s/%s#%s\n'%(domain[0],mydnsip,mydnsport))
#fs.write('ipset=/.%s/gfwlist\n'%domain[0]) #因为我采用的时国外ip走代理的方式,所以不需要这行。
else:
print 'no valid domain in this line: ' + line
tfs.close()
fs.close();
print 'moving generated file to dnsmasg directory'
shutil.move(outfile, rulesfile)
print 'restart dnsmasq...'
print os.popen('/etc/init.d/dnsmasq restart').read()
print 'done!'
|
gfwlist的网址访问不到的话,可以在海外VPS上生成gfwlist.conf后复制回国内VPS。
service dnsmasq start
命令开启dnsmasq服务。注意配置strongswan的DNS为本机的公网IP。
最好修改/etc/resolve.conf
文件,把默认的DNS改为127.0.0.1
.
至此,用手机客户端测试,ip138.com显示IP为阿里云,whatismyip.com显示IP为海外VPS。
如果全部按照我上文搭建中转服务器,现在有个问题是国内这台服务器是无法跨越长城的。因为这台服务器另有他用,而且我也没有让它也支持跨越长城的需求,所以这个我就不折腾了。