iOS8 为我们带来了第三方输入法和 App Extentions,越狱的需求越来越少。而且自从在 Bither 里存了一些比特币之后,我就越来越关注手机系统的安全性。现在真的是能不越狱就尽量不越了。那么不越狱的话怎么满足我自由的访问互联网的需求呢?
OS X,PC 或者 Android 翻墙,请移步 Shadowsocks。
我目前在 iPhone 上所用的翻墙方案包含如下两部分 :
- 自动连接并区分墙内外流量的 IPsec VPN
- 避开 GFW 污染,并尽量就近解析域名的 DNS
以下步骤需要你有自己的 VPS。
以下操作基于 Ubuntu,以非 root 用户 ssh 登录。其它发行版应该只有包管理和防火墙部分稍有不同。
IPsec VPN
我是在 Twitter 上看到 Justin 说 Anyconnect 在 iOS8 上变的特别慢之后,才开始考虑停掉 ocserv 换用 IPsec VPN 的,IPsec 的配置方法参考了 Justin 的文章。由于 IKEv2 在 iOS8 上有 Bug,所以这里还是用的 IKEv1 协议。不过如果 Apple 在哪个版本修复了 IKEv2 的话,现在这个配置方式也是很容易迁移到 IKEv2 的。
编译 Strongswan
apt-get 里的 Strongswan 版本低,所以这里需要自行编译最新版的 Strongswan。
检查依赖
如果是基于 OpenVZ 的 VPS 的话,先要检查一下有没有 TUN Device。输入下面的命令。非 OpenVZ 可以跳过这一步。
cat /dev/net/tun
如果执行结果如下,那么就可以安装 Strongswan 了。否则的话,去给 VPS 提供商发 Ticket 看能不能解决吧。
cat: /dev/net/tun: File descriptor in bad state
下载源码
wget http://download.strongswan.org/strongswan.tar.gz && tar zxvf strongswan*
cd strongswan*
安装编译所需的包
sudo apt-get build-dep strongswan
sudo apt-get install libgmp3-dev openssl libssl-dev
编译
Strongswan 在 configure
时要对 OpenVZ 特殊处理。
非 OpenVZ 执行如下命令。
./configure --sysconfdir=/etc --disable-sql --disable-mysql --disable-ldap --enable-dhcp --enable-eap-identity --enable-eap-mschapv2 --enable-md4 --enable-xauth-eap --enable-eap-peap --enable-eap-md5 --enable-openssl --enable-shared --enable-unity --enable-eap-tls --enable-eap-ttls --enable-eap-tnc --enable-eap-dynamic --enable-addrblock --enable-radattr --enable-nat-transport --enable-kernel-netlink
OpenVZ 要加上 --enable-kernel-libipsec
参数,完整命令如下。
./configure --sysconfdir=/etc --disable-sql --disable-mysql --disable-ldap --enable-dhcp --enable-eap-identity --enable-eap-mschapv2 --enable-md4 --enable-xauth-eap --enable-eap-peap --enable-eap-md5 --enable-openssl --enable-shared --enable-unity --enable-eap-tls --enable-eap-ttls --enable-eap-tnc --enable-eap-dynamic --enable-addrblock --enable-radattr --enable-nat-transport --enable-kernel-netlink --enable-kernel-libipsec
configure
完之后就可以 make
和 install
了
make && sudo make install
生成证书
这里是使用的自签名证书,需要你把 CA 证书用邮件发到 iOS 设备上安装才行。如果要用 ssl 证书,可以参考 wzxjohn 的文章。
Strongswan 的证书都是要放在 /etc/ipsec.d/
里的,不过我们是非 root 登录的,不方便直接操作这个目录,可以现在 home 目录下新建个目录来生成证书,等都生成好了再复制到 /etc/ipsec.d/
就行了。
mkdir ~/ipsec_cert && cd ~/ipsec_cert
生成证书用到的命令比较多,我写好了 script 来做这件事
生成服务器证书
记得把我这里写的 emptyzone.github.io
换成你自己的 Server IP 或者是域名。你打算用域名访问 VPN 就写 域名,打算用 IP 访问就写 IP,别写错了。
wget https://gist.githubusercontent.com/songchenwen/14c1c663ea65d5d4a28b/raw/cef8d8bafe6168388b105f780c442412e6f8ede7/server_key.sh
sh server_key.sh emptyzone.github.io
生成客户端证书
把 gary
和 gary@gmail.com
换成你自己的用户名和 email。这个脚本执行完会生成好导入 iOS 时需要的 p12
证书文件,最后提示你输入的密码就是用来加密它的,在往 iOS 导入时输入相同的即可。
wget https://gist.githubusercontent.com/songchenwen/14c1c663ea65d5d4a28b/raw/54843ae2e5e6d1159134cd9a90a08c31ff5a253d/client_key.sh
sh client_key.sh gary gary@gmail.com
执行完成后可以把以用户名开头的 .p12
文件 和 cacerts/strongswanCert.pem
下载到本地来备用。
复制证书到 /etc/ipsec.d/
Strongswan 需要的是 cacerts/strongswanCert.pem
certs/vpnHostCert.pem
private/vpnHostKey.pem
这三个文件。
sudo cp cacerts/strongswanCert.pem /etc/ipsec.d/cacerts/strongswanCert.pem
sudo cp certs/vpnHostCert.pem /etc/ipsec.d/certs/vpnHostCert.pem
sudo cp private/vpnHostKey.pem /etc/ipsec.d/private/vpnHostKey.pem
配置 Strongswan
编辑 /etc/ipsec.conf
sudo vi /etc/ipsec.conf
config setup
# strictcrlpolicy=yes
# uniqueids = replace
# charondebug="cfg 2, dmn 2, ike 2, net 0" #要看Log时,取消注释本行
conn %default
keyexchange=ikev1
dpdaction=hold
dpddelay=600s
dpdtimeout=5s
lifetime=24h
ikelifetime=240h
rekey=no
left=emptyzone.github.io #这里换成你登录 VPN 用的域名或 IP,与生成证书时相同
leftsubnet=0.0.0.0/0
leftcert=vpnHostCert.pem
leftsendcert=always
right=%any
rightdns=8.8.8.8
rightsourceip=10.0.0.0/8
conn CiscoIPSec
rightauth=pubkey
rightauth2=xauth
auto=add
编辑 /etc/ipsec.secrets
sudo vi /etc/ipsec.secrets
#验证用户所需的信息
#用户名 : EAP "密码"
: RSA vpnHostKey.pem
gary : EAP "strongpassword"
配置防火墙 iptables
参考我的配置文件,重要的是开启 NAT 转发 开放
4500
500
端口和esp
协议
/etc/iptables.firewall.rules
注意别把自己的 ssh 端口关闭了
*filter
# Allow all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0
-A INPUT -i lo -j ACCEPT
-A INPUT -d 127.0.0.0/8 -j LOG --log-prefix "looback denied: " --log-level 7
-A INPUT -d 127.0.0.0/8 -j REJECT
# Accept all established inbound connections
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Allow all outbound traffic - you can modify this to only allow certain traffic
-A OUTPUT -j ACCEPT
# Allow HTTP and HTTPS connections from anywhere (the normal ports for websites and SSL).
-A INPUT -p tcp --dport 80 -j ACCEPT
# Allow ipsec
-A INPUT -p udp --dport 4500 --j ACCEPT
-A INPUT -p udp --dport 500 --j ACCEPT
-A INPUT -p esp -j ACCEPT
# Allow SSH connections
#
# The -dport number should be the same port number you set in sshd_config
#
-A INPUT -p tcp -m state --state NEW --dport 1010 -j ACCEPT
# Allow ping
-A INPUT -p icmp -j ACCEPT
# Log iptables denied calls
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
-A INPUT -j DROP
COMMIT
在 /etc/sysctl.conf
中开启 net.ipv4.ip_forward=1
编辑 /etc/network/if-pre-up.d/firewall
#!/bin/sh
/sbin/iptables-restore < /etc/iptables.firewall.rules
执行
sudo chmod +x /etc/network/if-pre-up.d/firewall
编辑 /etc/rc.local
在 exit 0
前加上,20.16.3.18
换成你的服务器 IP。
iptables -t nat -A POSTROUTING -j MASQUERADE
iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
iptables -A POSTROUTING -t nat -s 10.0.0.0/8 -j SNAT --to-source 20.16.3.18
现在可以重启一下服务器,然后执行 sudo iptables -L
,看看防火墙有没有添加对。
区分流量自动路由
是的,你没看错,IPsec IKEv1 也是支持自动路由的。IPsec 支持 SplitTunneling。iOS 上的 IPsec 客户端用的是 Racoon,这货支持 Unity Plugin。也就是说我们可以用 IP 列表来区分国内外流量了。
但是当启用了 SplitTunneling
之后,iOS 端就不会使用我们在服务器端配置的 DNS 了,所以我们还需要利用 Unity Plugin 的 Split DNS
来把抗干扰的 DNS 推给客户端。
IP 列表可能会经常维护,所以我希望把它和主配置文件分开放置。放在我的 home
目录里就好了。
修改 /etc/strongswan.conf
在文件最后加上一句 include /home/gary/ipsec_config/*.conf
,gary
换成你的 ssh 用户名,这样 strongswan 就会导入我的 ~/ipsec_config
目录下的所有 .conf
文件。
添加 IP 列表配置
这是我现在用的 IP 列表,目前除了 Instagram 经常有图片下载不下来以外,没有别的问题。这个 IP 列表 iOS 端估计是有数量上限的。我测试的结果是当数量大于 160 时,iOS 端会返回 IKE DELETE
。
28675
是 Split DNS
的 Key,我这里用上了 GFWList 中的所有顶级域名。
创建并编辑 ~/ipsec_config/attr.conf
charon {
plugins {
attr {
split-include = 63.140.32.0/19, 66.117.16.0/20, 66.235.0.0/16, 130.248.0.0/16, 153.32.0.0/16, 185.34.188.0/22, 192.147.117.0/24, 192.150.0.0/16, 192.243.224.0/20, 192.243.248.0/21, 193.104.215.0/24, 195.35.86.0/24, 208.77.136.0/22, 216.104.0.0/16, 8.18.144.0/23, 23.20.0.0/14, 27.0.0.0/22, 46.51.128.0/18, 46.51.192.0/20, 46.51.216.0/21, 46.51.224.0/19, 46.137.0.0/17, 46.137.128.0/18, 46.137.224.0/19, 50.16.0.0/14, 50.112.0.0/16, 54.0.0.0/8, 67.202.0.0/18, 72.21.192.0/19, 72.44.32.0/19, 75.101.128.0/17, 79.125.0.0/17, 87.238.80.0/21, 96.127.0.0/17, 103.246.148.0/22, 107.20.0.0/14, 122.248.192.0/18, 174.129.0.0/16, 176.32.64.0/19, 176.34.0.0/16, 178.236.0.0/20, 184.72.0.0/15, 184.169.128.0/17, 184.154.0.0/16, 185.48.120.0/22, 199.0.0.0/8, 203.83.220.0/22, 204.236.128.0/17, 204.246.160.0/20, 204.246.176.0/22, 204.246.180.0/23, 204.246.182.0/24, 204.246.184.0/21, 205.251.192.0/19, 205.251.224.0/20, 205.251.240.0/21, 205.251.248.0/22, 205.251.252.0/23, 205.251.254.0/24, 207.171.160.0/19, 216.137.32.0/21, 216.137.40.0/22, 216.137.44.0/23, 216.137.48.0/20, 216.182.224.0/20, 17.0.0.0/8, 63.92.224.0/19, 192.12.74.0/24, 192.42.249.0/24, 204.79.190.0/24, 104.16.0.0/12, 108.162.192.0/19, 108.162.224.0/20, 108.162.240.0/21, 108.162.248.0/22, 108.162.252.0/23, 108.162.254.0/24, 162.158.0.0/15, 173.245.48.0/20, 198.41.128.0/17, 204.93.177.0/24, 108.160.160.0/20, 205.189.0.0/24, 37.48.64.0/18, 85.17.0.0/16, 95.211.0.0/16, 31.13.24.0/21, 31.13.64.0/18, 66.220.144.0/20, 69.63.176.0/20, 69.171.224.0/19, 74.119.76.0/22, 173.252.64.0/18, 204.15.20.0/22, 23.235.32.0/20, 104.156.80.0/20, 208.71.104.0/22, 192.30.252.0/22, 8.6.48.0/21, 8.8.4.0/24, 8.8.8.0/24, 8.15.202.0/24, 8.34.208.0/20, 8.35.192.0/20, 23.236.48.0/20, 23.251.128.0/19, 64.9.224.0/19, 64.233.160.0/19, 66.102.0.0/20, 66.249.64.0/19, 70.32.128.0/19, 72.14.192.0/18, 74.125.0.0/16, 104.132.0.0/14, 104.154.0.0/15, 104.196.0.0/14, 107.167.160.0/19, 107.178.192.0/18, 108.59.80.0/20, 108.170.192.0/18, 108.177.0.0/17, 130.211.0.0/16, 142.250.0.0/15, 146.148.0.0/17, 162.216.148.0/22, 162.222.176.0/21, 172.217.0.0/16, 172.253.0.0/16, 172.16.16.1/32, 173.255.112.0/20, 192.158.28.0/22, 192.178.0.0/15, 207.223.160.0/20, 209.85.128.0/17, 216.58.192.0/19, 216.239.32.0/19, 205.196.120.0/22, 8.25.0.0/16, 192.133.76.0/22, 93.184.216.0/24, 59.24.3.0/24, 91.198.174.0/24, 185.15.56.0/22, 198.35.26.0/23, 198.73.209.0/24, 208.80.152.0/22, 66.155.8.0/21, 76.74.248.0/21, 192.0.64.0/18, 198.181.116.0/22
28675 = mil com tv fm za tw org info biz ca net ru au us de hk jp me uk io is it in li gov ly fr nu st asia im my xxx tk eu cc mobi se edu il kr ie ar nl cm ua es ph bz br be mp cz name lu ch su to no co nz sg ma vc am at la sh tl gd sk id pl mo tc hu
}
}
}
现在可以执行 sudo ipsec start --nofork
,试一下了。--nofork
参数会让 strongswan 在前台运行,调试时加上就可以了,平时就让它在后台运行吧。
开机自启
编辑 /etc/rc.local
在 exit 0
前加上 ipsec start
。
iOS 端自动配置文件
这里完全参考 Justin 的文章就可以了,他那有截图,我就只说一下步骤吧。
用 Apple Configurator 创建配置描述文件。
通用里的名称和标识符自己填好。
凭证里导入刚才下载好的客户端 .p12
证书。
VPN 里连接类型选 IPsec(Cisco)
,机器鉴定选证书,在下面选中刚才导入的证书。
导出这个配置描述文件,别签名。
用文本编辑器编辑。
找到这一段
<key>OnDemandEnabled</key>
<integer>1</integer>
改成下面这样
<key>OnDemandEnabled</key>
<integer>1</integer>
<key>OnDemandRules</key>
<array>
<dict>
<key>Action</key>
<string>Connect</string>
</dict>
</array>
另外,如果配置描述文件里只有 XAuthName
,没有 XAuthPassword
的话,可以自己加上这个 Key,然后把密码填上。
把这个配置描述文件,连同刚才下载好的服务器 CA 证书 strongswanCert.pem
一起,用邮件发到自己的 iOS 设备上,然后安装好,试一下吧。
DNS
使用刚才配置的 IPsec VPN 翻墙的话,就总会使用谷歌的 DNS 服务器做域名查询。这样很多明明国内有 CDN 的域名,却会解析到国外的 IP 上,造成访问缓慢。所以我要利用 pdnsd
来创建一个带缓存的域名解析服务。这里主要参考了这篇文章。
安装 pdsnd
sudo apt-get install pdnsd
配置 pdnsd
编辑 /etc/pdnsd.conf
global {
perm_cache = 8192;
cache_dir = "/var/cache/pdnsd";
run_as = "pdnsd";
server_ip = 0.0.0.0;
status_ctl = on;
paranoid = on;
query_method = udp_tcp;
tcp_server = on;
min_ttl = 1d;
max_ttl = 52w;
timeout = 10;
randomize_recs = on;
debug = off;
daemon = on;
verbosity = 1;
neg_rrs_pol = on;
neg_domain_pol = on;
par_queries = 4;
}
server {
label = "Unicom Beijing";
ip = 202.106.196.115, 202.106.46.151, 202.106.0.20, 202.106.195.68;
timeout = 10;
proxy_only = on;
caching = on;
randomize_servers = off;
reject_policy = fail;
reject_recursively = on;
include =
// Place Domains Below. Remember add a dot(".") at the start of domain, if you also want to redirect all subdomains!
".cn",
// Place Domains Above.
"DOMAIN.PADDING";
exclude =
// Place Domains Below. Remember add a dot(".") at the start of domain, if you also want to redirect all subdomains!
".darpa.mil", ".fxnetworks.com", ".hulu.com", ".huluim.com", ".muzu.tv", ".netflix.com"
// Place Domains Above.
"DOMAIN.PADDING";
reject = 118.5.49.6, 128.121.126.139, 159.106.121.75, 169.132.13.103, 188.5.4.96, 189.163.17.5, 192.67.198.6, 197.4.4.12, 202.106.1.2, 202.181.7.85, 203.161.230.171, 203.98.7.65, 207.12.88.98, 208.56.31.43, 209.145.54.50, 209.220.30.174, 209.36.73.33, 209.85.229.138, 211.94.66.147, 213.169.251.35, 216.221.188.182, 216.234.179.13, 23.89.5.60, 243.185.187.39, 249.129.46.48, 253.157.14.165, 37.61.54.158, 4.36.66.178, 46.82.174.68, 49.2.123.56, 54.76.135.1, 59.24.3.173, 64.33.88.161, 64.33.99.47, 64.66.163.251, 65.104.202.252, 65.160.219.113, 66.45.252.237, 72.14.205.104, 72.14.205.99, 74.125.127.102, 74.125.155.102, 74.125.39.102, 74.125.39.113, 77.4.7.92, 78.16.49.15, 8.7.198.45, 93.46.8.89;
}
server {
label = "Google"; // Primary
timeout = 3;
ip = 8.8.8.8, 8.8.4.4, 208.67.222.222, 208.67.220.220;
proxy_only = on;
caching = on;
randomize_servers = on;
}
rr {
name=localhost;
reverse=on;
a=127.0.0.1;
owner=localhost;
soa=localhost,root.localhost,42,86400,900,86400,86400;
}
/* vim:set ft=c: */
这里默认使用北京联通的域名解析服务器,你也可以换成更适合自己的。当遇到被墙域名或者收到了被污染的 IP 时,再使用谷歌和 OpenDNS 的服务器。
执行 sudo service pdnsd start
来启动 pdnsd。
为使用 pdnsd 配置 Strongswan
我们刚才配置的 iptables 中并没有开启 DNS 服务需要的 53 端口。这个端口如果完全开放的话,VPS 提供商可能会给我们发安全警告。所以我们要利用 Strongswan 的 updown script
来完成对 iptables 的设置。
创建 leftupdown 脚本
我把这个脚本放在了 ~/ipsec_config/leftupdown
,你也可以放在你喜欢的位置。
文件内容如下。
#!/bin/sh
PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin"
export PATH
echo "updown executing"
ipsec _updown
case "$PLUTO_VERB:$1" in
up-client:)
iptables -C INPUT -s $PLUTO_PEER -p tcp -m tcp --dport 53 -j ACCEPT
exist=$?
if [ $exist -eq 0 ];then
echo "updown script rule already exists for $PLUTO_PEER"
else
echo "updown script up client $PLUTO_PEER"
iptables -I INPUT 4 -s $PLUTO_PEER -p tcp -m tcp --dport 53 -j ACCEPT
iptables -I INPUT 4 -s $PLUTO_PEER -p udp -m udp --dport 53 -j ACCEPT
iptables -I INPUT 4 -s $PLUTO_PEER -p tcp -m tcp --sport 53 -j ACCEPT
iptables -I INPUT 4 -s $PLUTO_PEER -p udp -m udp --sport 53 -j ACCEPT
fi
;;
down-client:)
echo "updown script down client $PLUTO_PEER"
iptables -D INPUT -s $PLUTO_PEER -p tcp -m tcp --dport 53 -j ACCEPT
iptables -D INPUT -s $PLUTO_PEER -p udp -m udp --dport 53 -j ACCEPT
iptables -D INPUT -s $PLUTO_PEER -p tcp -m tcp --sport 53 -j ACCEPT
iptables -D INPUT -s $PLUTO_PEER -p udp -m udp --sport 53 -j ACCEPT
;;
esac
保存好文件后,修改文件权限 chmod 777 ~/ipsec_config/leftupdown
。
修改 /etc/ipsec.conf
把 rightdns
改成 VPS 的 IP。在 %default
里添加一行 leftupdown=/path/to/your/leftupdown
,注意这里把路径换成刚才创建的 leftupdown
脚本的绝对路径。
执行 sudo ipsec reload
,然后客户端重新连接一下,试试效果吧。