(credit: pixiv)
换而言之,让我们来谈谈有中国特色的互联网生活。
更新2015-05-31:新服务器,正好又跑了一遍设置,修正了一些流程。
更新2015-01-23:新服务器,正好又跑了一遍设置,修正了一些流程。
更新2014-10-22:补充了配置上的具体步骤,修正和最新版不同的设定。
事出有因,最近我的主力手机从自由度较高的Android平台切换为土豪度较高的iOS平台,遇到的第一个问题就是科学上网的难题。和Android平台八仙过海的客户端不同,要在iOS上使用非VPN服务非常困难,更别说没有越狱的设备。
鉴于我个人受够了Android的版本碎片化(硬件原因无法升级OS),让iOS留在旧版显然不合我趣味。找个靠谱的VPN解决方案成为唯一选项。
用什么?OpenVPN作为流行的选项,似乎已被老爷们摸透看清,连流量混淆或Stunnel外套都已经被当成主要攻略对象来推倒(或者说流行教程的配置倍受照顾)。看来是时候选个不怎么流行的解决方案。
OpenConnet Server(ocserv)是朋友给我的建议,它通过实现Cisco的AnyConnect协议,用DTLS作为主要的加密传输协议。我认为它的主要好处在于——
- AnyConnect的VPN协议默认使用UDP DTLS作为数据传输,但如果有什么网络问题导致UDP传输出现问题,它会利用最初建立的TCP TLS通道作为备份通道,降低VPN断开的概率。
- AnyConnect作为Cisco新一代的VPN解决方案,被用于许多大型企业,这些企业依赖它提供正常的商业运作,这些正常运作对应的经济效益(读作GDP),是我们最好的伙伴。
- OpenConnet的架设足够麻烦,我的意思是,如果你不是大型企业,你会用AnyConnect的概率无限趋近于零。再者,如果它足够简单,我就不用写这篇文章了。
至于它的自定义路由表支持,我觉得都是次要了。
介绍到此,让我们按步骤干好事情。
(下文选用最新的Ubuntu 14.04 LTS和OCServ 0.10.5作为标准环境,但我会尽量提供不依赖版本的步骤与建议。)
编译OCserv
到官方站点找最新的OpenConnect Server版本。
curl -O ftp://ftp.infradead.org/pub/ocserv/ocserv-0.10.5.tar.xz
tar xvf ocserv-0.10.5.tar.xz
cd ocserv-0.10.5
看下README文件提及的编译依赖。理论上只有libgnutls-dev和libreadline-dev是必须的,但我们还是把可选的功能都带上。
(顺便一提,如果你在Ubuntu 14.04或更早版本上,libgnutls-dev的版本还是2.x,需要用libgnutls28-dev获取3.x的GnuTLS才能支持OCserv。)
sudo apt-get install build-essential pkg-config libgnutls28-dev libreadline-dev libseccomp-dev libwrap0-dev libnl-nf-3-dev liblz4-dev
编译并安装。
./configure
(在我测试的环境里,最终报告只有systemd、dbus、PAM、Radius、GSSAPI的结果为no,如果你发现其他项目显示no,不必担心,你的环境可能安装了可选的包导致出现不同结果。目前只有libgnutls28-dev是必须的模块。)
(在Ubuntu 14.04上,liblz4-dev缺了pkg-config文件,所以即便安装了liblz4-dev,LZ4支持仍显示no。LZ4支持是ocserv 0.10下的新功能,装不装对使用没大碍,如果需要,你得想办法手动安装LZ4。)
make
sudo make install
配置OCserv
我们希望做到的,是无需用户名与密码的客户端证书验证登陆。但在此之前,让我们先测通更简单的密码登录模式。首先让我们把CA证书与服务器证书生成好,具体步骤官方文档也有——
先准备好certtool
命令。
sudo apt-get install gnutls-bin
创建CA
mkdir certificates
cd certificates
CA模板,创建ca.tmpl,按需填写,这里的cn和organization可以随便填。
cn = "Your CA name"
organization = "Your fancy name"
serial = 1
expiration_days = 3650
ca
signing_key
cert_signing_key
crl_signing_key
CA密钥
certtool --generate-privkey --outfile ca-key.pem
CA证书
certtool --generate-self-signed --load-privkey ca-key.pem --template ca.tmpl --outfile ca-cert.pem
同理,我们用CA签名,生成服务器证书。先创建server.tmpl模板。这里的cn项必须对应你最终提供服务的hostname或IP,否则AnyConnect客户端将无法正确导入证书。
cn = "Your hostname or IP"
organization = "Your fancy name"
expiration_days = 3650
signing_key
encryption_key
tls_www_server
Server密钥
certtool --generate-privkey --outfile server-key.pem
Server证书
certtool --generate-certificate --load-privkey server-key.pem --load-ca-certificate ca-cert.pem --load-ca-privkey ca-key.pem --template server.tmpl --outfile server-cert.pem
将CA,Server证书与密钥复制到以下文件夹
sudo cp ca-cert.pem /etc/ssl/private/my-ca-cert.pem
sudo cp server-cert.pem /etc/ssl/private/my-server-cert.pem
sudo cp server-key.pem /etc/ssl/private/my-server-key.pem
剩下的就是OCServ配置文件了。同样的,参考官方文档是最佳选项,但为了方便起见,这是你需要注意的一些设置。回到ocserv-0.10.5的文件夹下,将配置文件复制到OCserv默认读取的位置。
sudo mkdir /etc/ocserv
sudo cp doc/sample.config /etc/ocserv/ocserv.conf
确保以下配置正确
# 登陆方式,目前先用密码登录
auth = "plain[/etc/ocserv/ocpasswd]"
# 允许同时连接的客户端数量
max-clients = 4
# 限制同一客户端的并行登陆数量
max-same-clients = 2
# 服务监听的IP(服务器IP,可不设置)
listen-host = 1.2.3.4
# 服务监听的TCP/UDP端口(选择你喜欢的数字)
tcp-port = 9000
udp-port = 9001
# 自动优化VPN的网络性能
try-mtu-discovery = true
# 确保服务器正确读取用户证书(后面会用到用户证书)
cert-user-oid = 2.5.4.3
# 服务器证书与密钥
server-cert = /etc/ssl/private/my-server-cert.pem
server-key = /etc/ssl/private/my-server-key.pem
# 客户端连上vpn后使用的dns
dns = 8.8.8.8
dns = 8.8.4.4
# 注释掉所有的route,让服务器成为gateway
#route = 192.168.1.0/255.255.255.0
# 启用cisco客户端兼容性支持
cisco-client-compat = true
创建一个登陆用的用户名与密码。
sudo ocpasswd -c /etc/ocserv/ocpasswd your-username
这样OCserv就基本配置好了。但如果你和我一样强化过服务器安全,还得为服务器上开些端口才行。以Linode的安全配置为例,我们需要加入和修改以下内容。
sudo nano /etc/iptables.firewall.rules
打开OCserv对应的TCP/UDP端口(别忘了对应你选择的端口数)
-A INPUT -p tcp -m state --state NEW --dport 9000 -j ACCEPT
-A INPUT -p udp -m state --state NEW --dport 9001 -j ACCEPT
注释这行,允许转发
# -A FORWARD -j DROP
启用NAT
*nat
-A POSTROUTING -j MASQUERADE
COMMIT
完成之后导入新配置并检查配置正确。
sudo iptables-restore < /etc/iptables.firewall.rules
sudo iptables -L
sudo iptables -t nat -L
如果你之前没有配置服务器启动时自动导入这个设置
sudo nano /etc/network/if-pre-up.d/firewall
输入以下内容
#!/bin/sh
/sbin/iptables-restore < /etc/iptables.firewall.rules
最后我们需要打开IPv4的流量转发。
sudo nano /etc/sysctl.conf
启用此项
net.ipv4.ip_forward=1
并刷新配置
sudo sysctl -p /etc/sysctl.conf
测试OCserv
在服务器端启动OpenConnect Server。
sudo ocserv -f -d 1
如果服务没错误退出,是时候来测测客户端了。假设你使用iOS,下载Cisco AnyConnect。
在Connections下加入新的VPN配置,在服务器地址栏目上填入对应的IP/Hostname和TCP端口(我们的例子就是1.2.3.4:9000)
然后到设置标签页下暂时禁用“阻止不信任的服务器”选项。首次连接,AnyConnect会提示你这是不信任证书,如果你之前的服务器证书模板的cn没写错的话(我们的例子是1.2.3.4),你可以接受并导入该证书(可在诊断标签页的证书菜单里的服务器证书列表看到)。以后即便启用“阻止不信任的服务器”选项,也不会报错了(和SSH首次登陆类似)。
确定VPN连接正常并可以科学上网后,我们可以接着提高网络生活质量。
自动化OCserv
假如现有的配置有哪里让人不大满意,大概是这两点——
- OCserv的服务最好会自动跑,进程挂了也自动恢复。
- AnyConnect每次都要输入密码很麻烦,最好用客户端证书验证。
为OCserv写个简单的upstart脚本。
cd /etc/init.d
sudo ln -s /lib/init/upstart-job ocserv
cd /etc/init
sudo nano ocserv.conf
放入以下内容
#!upstart
description "OpenConnect Server"
start on runlevel [2345]
stop on runlevel [06]
respawn
respawn limit 20 5
script
exec start-stop-daemon --start --pidfile /var/run/ocserv.pid
--exec /usr/local/sbin/ocserv -- -f >> /dev/null 2>&1
end script
这样就可以用以下方式启动/暂停服务
sudo service ocserv start
sudo service ocserv stop
为AnyConnect建个客户端证书
和服务器端证书的步骤基本相同。回到之前的certificates文件夹。
创建user.tmpl
cn = "some random name"
unit = "some random unit"
expiration_days = 365
signing_key
tls_www_client
User密钥
certtool --generate-privkey --outfile user-key.pem
User证书
certtool --generate-certificate --load-privkey user-key.pem --load-ca-certificate ca-cert.pem --load-ca-privkey ca-key.pem --template user.tmpl --outfile user-cert.pem
然后要将证书和密钥转为PKCS12的格式。
certtool --to-p12 --load-privkey user-key.pem --pkcs-cipher 3des-pkcs12 --load-certificate user-cert.pem --outfile user.p12 --outder
然后我们要通过URL将user.p12文件导入AnyConnect,具体位置在诊断标签页的证书栏目下。如果你的服务器已经有Nginx/Apache服务,只要传到一个可以访问的URL路径下即可。另一个选择是将user.p12复制到本地,建立一个HTTP服务(例如用node.js)。
导入成功之后,将对应的VPN设置的高级设置部分的证书栏目,改为导入的这张证书。
最后我们要调整下OCserv的配置——
sudo nano /etc/ocserv/ocserv.conf
修改以下内容
# 改为证书登陆,注释掉原来的登陆模式
auth = "certificate"
# 证书认证不支持这个选项,注释掉这行
#listen-clear-file = /var/run/ocserv-conn.socket
# 启用证书验证
ca-cert = /etc/ssl/private/my-ca-cert.pem
重启OCserv服务,确认VPN无需密码就可以正常登陆。
小结
这篇笔记是在完成配置之后靠回忆写下的,仅仅做到“可用”,没做更多优化与安全配置。如有什么疏漏(很可能有,毕竟环境不可能完全一致),欢迎在留言栏相互协助。祝各位的iPhone生活美满~
参考阅读:
写少了几处iptables?
应该是iptables -A吧
不,我是先存一个文本文件,再倒入iptable。
抱歉没仔细看
编译部署可以直接使用 https://github.com/wppurking/ocserv-docker
ps 你这模版对手机浏览真是太不友好了
我怎么没看出什么问题……
我这里在CentOS上也部署成功了(编译安装部分参考https://botu.me/install-ocserv-on-centos6/)。其余启用证书部分按照店长的步骤做下来基本没有问题,就是在启用证书这里需要修改服务器端配置文件ocserv.conf,把里面的session-control = true 改成session-control = false,否则会导致worker进程异常中止
奇怪了,session-control默认不是被注释掉的么?
大多数配置都不需要用它,它的默认值不是false?
我从下载的source包的doc目录里找的ample.config , 默认是启用auth = “plain[./sample.passwd]”,并且session-control = true 开启
理解了,我的教程基于doc/sample.config
看了下0.8.6包里的doc/sample.config文件,session-control = true
这可能是个bug,见commit log
http://git.infradead.org/ocserv.git
这个bug已经修复,参见:
http://www.infradead.org/ocserv/changelog.html
Version 0.8.7 (released 2014-10-26)
Networking sockets were switched to non-blocking in worker process.
Fixed a crash when session control is enabled but not password authentication. Reported by George Panda.
使用证书登录的话需要取消以下项目的注释, 0.8.4 版如此
cert-user-oid = 0.9.2342.19200300.100.1.1
cert-group-oid = 2.5.4.11
Hmm,UID并不是必须的X.509证书模板选项,例如我们的例子里就只设了CN。
(cert-user-oid = 0.9.2342.19200300.100.1.1对应的是X.509里的UID)
PS:在本文对应的配置场景下,并没有特殊的user/group设置,读取UID或CN并没有什么实质作用,默认注释也不影响使用。
有点不明白
ocserv的路由表设置非常奇怪
在windows下,如果设置为default gateway模式,连上vpn后,其路由表设置会导致windows下client无法直接与vpn服务器进行通信(比如不能ssh vpn服务器或者访问架设在vpn服务器上的网站)
但linux,android,ios则不会出现这种问题,不知道有没有解决方法
查到解决方法了
要全局路由的话,设置里不要直接注释掉全部route
而是改成
route = 0.0.0.0/128.0.0.0
route = 128.0.0.0/128.0.0.0
来避开cisco 客户端的奇怪行为
准确的说,OCServ需要的是3.1的GnuTLS,而libgnutls28-dev只能获取到3.0的
ubuntu 12.04下尝试升级GnuTLS到3.1,结果陷入了依赖包升级地狱,有不少东西都需要升级,折腾了半天没有搞定,准备直接上14.04的ubuntu了
OpenVPN现在连Stunnel外套都不行了么…目前我就在用这个方案…求告知详细…
太可怕
奇怪,我可以连接上VPN,但无法访问任何外部网站。
已经打开IPv4的转发了,net.ipv4.ip_forward=1,使用PPTP连接是可以访问外网的。
唯一和店长设置不同的地方就是没设置iptables, 因为我没有设置防火墙。
请问这种情况该从哪里开始找原因呢?
我错了,还是要设置iptables才可以
能使用 正规的证书 免密码登录吗,比如 startssl 的 免费证书
这里的cn项必须对应你最终提供服务的hostname或IP,否则AnyConnect客户端将无法正确导入证书。
我这里写上我VPS的IP地址也不能导入,换成hostname之后也不能导入,问一下是什么原因?谢谢
具体要看报了什么错。
android如何配置证书登录?pc ios端都可用了 剩下android总是登录不了 只能帐户密码登录
学习下最新版本的编译配置
certtool生成p12文件要加上参数–outder 才能导入到android 和 IOS
所以我们推荐使用openssl,因为certtool的p12有兼容问题,至少在AnyConnect如此。
不是兼容问题吧,只是这儿:https://support.google.com/nexus/answer/2844832?hl=zh-Hans说了Android 支持以扩展名为 .crt 或 .cer 的文件格式保存的 DER 编码 X.509 证书。加了–outder会输出 DER 编码的证书 导入就正常了 IOS大概也一样吧 其实用那种生成都一样
我和ocserv作者(也是gnutls作者)折腾了半天没搞懂iOS上为什么无法导入certtool的p12证书,他手里又没iOS设备,所以干脆走openssl。
我用certtool生成的可以直接导入啊 ??
店长,sudo iptables-restore < /etc/iptables.firewall.rules 一步,总是提示Bad argument `*nat',是什么问题?ubuntu 14.04, DigitalOcean
研究下你复制粘贴有没出错,`*nat’。以及看看iptables的输出。
sudo iptables -t nat -L
你好,非常感谢你写了这篇文章,我按照这个教程一路下来,几乎就要成功了,在最后遇到了一点问题:我用 `sudo ocserv -f -d 1` 成功的启动了 ocserv ,并且用 iOS 上的 AnyConnect 客户端连上了服务器,但是打不开任何网站了,请问你遇到过这个问题吗?是我哪里出了疏漏吗?
对了,在配置过程中我发现 certtool 并不是本来就在 Ubuntu 里安装好的,你可以在安装依赖那小节里再加一个 gnutls-bin
谢谢
啊,不好意思,我后来看了评论,发现自己也犯了和 @笑笑猫 一样的错误,即使之前 iptables 什么都没有配置过,文中的步骤还是要执行的
对了……我那个什么错误都没报……
友情提示下,当前的ocserv 0.9.0和0.9.0.1有一个连接断开后无法用auth cookie自动重连的bug。git master上已经修好两周了(我测试有效)。但未见官方发0.9.1包,所以使用时请注意。
请问下这个应该如何修复呢?
从git clone 一个然后自己build吧
我配置完之后,ios手机能正常连接,但是电脑anyconnect客户端连接上自动断开,显示MTU太小,有解决方案么?