使用 ocserv 搭建企业级 OpenConnect VPN 网关并使用 Let's Encrypt 证书
AnyConnect 和 OpenConnect
本文于 2019-02-21 更新,修改了关于申请 SSL 证书的章节。
Wikipedia 上描述 OpenConnect 如下:
OpenConnect is an open-source software application for connecting to virtual private networks (VPN), which implement secure point-to-point connections.
It was originally written as an open-source replacement for Cisco’s proprietary AnyConnect SSL VPN client,[2] which is supported by several Cisco routers. As of 2013, the OpenConnect project also offers an AnyConnect-compatible server,[3] and thus offers a full client-server VPN solution.
可以简要地看出,OpenConnect 原本是由于 AnyConnect 有只能运行在 Cisco 设备上限制而开发出来的一个多系统支持的开源 VPN 实现方式,属于 SSL VPN,需要一个有效的 SSL 证书。
本文实行简单粗暴的原则,记录了一个 Ubuntu 服务器最小化搭建 ocserv(OpenConnect 服务端) 服务的过程,所以:不使用证书登录验证(使用用户名 + 密码组合),SSL 使用 Let’s Encrypt(而非网上许多文章所介绍的自签发)。
Why OpenConnect
可能有些小伙伴看到本文长度会问了,为什么要搞这么复杂?直接 ss-server 或者 OpenVPN 一键脚本安装不好么?
原因有三:
- 我们需要的是安全内网访问,不是快速地绕过防火墙… 而且后期需要加入证书认证
- OpenVPN 协议特征过于明显,虽然 AnyConnect 协议特征也十分明显,但是由于目前只有一些大厂在用,一般而言直接拨位与海外的 VPN 网关不容易受到干扰或受到的干扰较小
- 对于例如 iOS/BlackBerry BBOS 系统而言,一般自带 AnyConnect 连接工具
本例中:
- 一台全新的 Ubuntu 18.04 LTS(主要是考虑到 80 端口未被占用,给后文中获取 SSL 证书的方法提供可能)
- 域名为:vpn.example.com,并且已经做好了解析到服务器 IP
- 服务器 IP 为:1.2.3.4
2018-09-12 更新:如果 80 端口被占用了可以考虑使用 DNS Challenge 的方法获取 Let’s Encrypt 证书,相关步骤可以参考 《使用 Google Cloud Platform 的 Storage 托管静态站点并通过 Google CDN 加速》
安装 ocserv & 准备系统
网上许多方法都是通过手动编译源代码包的方式安装,然而现在至少对于 Debian 系的系统来说已经有了编译好的软件包了,详情见 Distribution Status,对于 Debian 系服务器来说(比如本例的 Ubuntu)直接一条指令即可(非常感谢维护这个包的:Aron Xu,Liang Guo 和 Mike Miller):
$ sudo apt install ocserv -y
之后我们需要打开系统的转发功能,在 /etc/sysctl.conf
中加入如下行:
net.ipv4.ip_forward=1
通过
$ sysctl -p
保存。
打开 NAT 功能:
# iptables -t nat -A POSTROUTING -j MASQUERADE
配置 Let’s Encrypt 证书
ocserv
需要 SSL 证书(用来加密连接流量,保证连接安全,放心,这一步不复杂),网上许多教程中使用的是自签发证书,方法复杂且容易被 MITM 攻击,好在现在有 Let’s Encrypt 可以免费为自己域名添加证书,本例中使用 certbot
来获取一个 Let’s Encrypt 证书。
下载certbot
,方法很多,在本例中为:
$ sudo apt-get update
$ sudo apt-get install software-properties-common
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt-get update
$ sudo apt-get install certbot
其他系统请参考 Certbot 官方网站。
这一步比较 Tricky,请仔细阅读:certbot 获取 SSL 证书有多种方式,例如它可以在你机器上起一个临时的网页服务器,并且让自己的 Authority 来尝试连接临时服务器用来确认你机器的所有权,也可以通过 DNS 设置 TXT 记录的方式来验证,以下方式使用的是开一个临时服务器的方式来获取,如果希望通过 DNS 修改 TXT 记录的方式获取,请参考《使用 Google Cloud Platform 的 Storage 托管静态站点并通过 Google CDN 加速》一文中的“获取 SSL 证书章节”。
此外,有热心读者指出:ocserv 程序在安装后会使用 443 端口导致开启临时网页服务器的时候失败,读者给出的建议如下:
在进行
certbot
获取证书之前,先以管理员权限修改/lib/systemd/system/ocserv.socket
配置文件,将其中的两个443端口号修改为其他未被占用的端口号后,再运行certbot
即可,这样做的好处是,可以利用certbot
的自动证书续期功能。另外,
/lib/systemd/system/ocserv.socket
中指定的端口号无需与/etc/ocserv/ocserv.conf
中的端口号保持一致。在使用 OpenConnect 或者 AnyConnect 客户端时,使用在/lib/systemd/system/ocserv.socket
中指定的端口号即可。
非常感谢这位读者的邮件,欢迎大家在测试的时候进行尝试~
$ certbot certonly
会看到:
Saving debug log to /var/log/letsencrypt/letsencrypt.log
How would you like to authenticate with the ACME CA?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: Spin up a temporary webserver (standalone)
2: Place files in webroot directory (webroot)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):
由于我们仅仅是想要一个证书,这里选择 1,让 certbot
来搞定证书获取的过程,之后输入自己的域名,比如本例中的 vpn.example.com
,稍等片刻应该可以看到类似如下的输出(记住证书存放的地址,后面会用到):
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/vpn.example.com/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/vpn.example.com/privkey.pem
Your cert will expire on 2018-11-11. To obtain a new or tweaked
version of this certificate in the future, simply run certbot
again. To non-interactively renew *all* of your certificates, run
"certbot renew"
配置 ocserv
默认安装好后在/etc/ocserv/
下有一个很长的配置文件ocserv.conf
,着重注意以下配置字段:
# 登录方式,使用用户名密码登录,密码文件稍后生成
auth = "plain[/etc/ocserv/ocpasswd]"
# 允许同时连接的客户端数量
max-clients = 4
# 限制同一客户端的并行登陆数量
max-same-clients = 2
# 服务监听的 TCP/UDP 端口(默认为 443)
tcp-port = 443
udp-port = 443
# 自动优化 MTU,尝试改善网络性能
try-mtu-discovery = true
# 服务器证书与密钥,就是上一步中生成的证书和私钥的位置
server-cert = /etc/letsencrypt/live/vpn.example.com/fullchain.pem
server-key = /etc/letsencrypt/live/vpn.example.com/privkey.pem
# 服务器域名
default-domain = vpn.example.com
# 客户端连上 vpn 后使用的 DNS,这里使用 Cloudflare 的 1.1.1.1
dns = 1.1.1.1
# 注释掉所有的 route 和 no-route,让服务器成为 gateway
#route = 192.168.1.0/255.255.255.0
#no-route = 192.168.5.0/255.255.255.0
# 启用 Cisco 客户端兼容性支持
cisco-client-compat = true
由于使用用户名密码登录,我们需要生成一个密码文件,指令如下:
$ ocpasswd -c /etc/ocserv/ocpasswd <用户名>
此时会要求你输入两边密码,如果需要再添加用户只需重复上述指令即可。
配置好后启动 VPN:
$ ocserv -c /etc/ocserv.conf
确认已经开启:
root@vpn:/etc/ocserv# netstat -tulpn | grep 443
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 1987/ocserv
tcp6 0 0 :::443 :::* LISTEN 1987/ocserv
udp 0 0 0.0.0.0:443 0.0.0.0:* 1987/ocserv
udp6 0 0 :::443 :::* 1987/ocserv
Connecting Through VPN
配置好 VPN 后让自己的所有服务器全部拨上 VPN:
# openconnect https://vpn.example.com/
对于个人用户访问,这里以黑莓 Passport 为例(请无视那个现在并不存在企业名称),截图如下:
拨通后可以看到自己的内网 IP :
然后,开始在 VPN 的保护下畅游自己的大内网吧~
Off-Topic
如果直接用浏览器去访问 VPN 网关(比如本例中:https://vpn.example.com)的话,返回的是如下 HTML 内容:
<?xml version="1.0" encoding="UTF-8"?>
<config-auth client="vpn" type="auth-request">
<version who="sg">0.1(1)</version>
<auth id="main">
<message>Please enter your username.</message>
<form method="post" action="/auth">
<input type="text" name="username" label="Username:" />
</form></auth>
</config-auth>
此外如果开启了 ocserv
之后在 sudo
的时候卡住并提示:“sudo: unable to resolve host vpn: Resource temporarily unavailable” 的话,着重关注一下自己的/etc/hosts
文件中是否包含一行:
127.0.0.1 localhost