使用 ocserv 搭建企业级 OpenConnect VPN 网关并使用 Let’s Encrypt 证书

本文于 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

参考连接

  1. 使用 ocserv 搭建 Cisco Anyconnect 服务器
  2. Setup OpenConnect VPN Server for Cisco AnyConnect on Ubuntu 14.04 x64