在Arch Linux上部署OpenConnect VPN

OpenConnect VPN Server,也称为 ocserv ,采用OpenConnect SSL VPN协议,并且和Cisco AnyConnect SSL VPN协议的客户端兼容。目前不仅加密安全性好,而且客户端可以跨平台,主流操作系统以及手机操作系统都可以使用。

选择OpenConnect VPN Server( ocserv )的主要原因如下:

  • SSL VPN协议安全性较好,被GFW干扰的可能性较低(GFW不太可能完全屏蔽SSL流量,毕竟HTTPS是现代互联网的主流)

  • 跨平台通用客户端,这样我可以在Linux,macOS,Windows以及Android,iOS平台使用

备注

由于我将VPS服务器从 Ubuntu Linux 更改到 Arch Linux ,所以重新部署Ocserv服务(OpenConnect VPN)

我力求在 OpenConnect VPN 基础上整合近期的部署改进,以实现完善的个人VPN部署 越过长城

准备工作

  • 需要购买一台海外VPS

  • 需要一个域名,在注册域名中添加一条记录指向新购买VPS的IP地址( vpn.example.com 解析到将要部署的服务器IP地址)

备注

使用Let’s Encrypt签发的证书是针对域名的:

  • 使用 certbot 生成证书时候,Let's Encrypt会通过你签发证书的域名访问该域名对应IP的80端口进行验证,所以必须准备好部署服务器的域名解析

  • 如果暂时无法准备好域名解析,那么可以自己手工签发一个证书临时使用

安装ocserv

Arch Linux 发行版只包括了 openconnect 客户端,但是没有直接提供 ocserv 服务端。在 Arch Linux上安装 ocserv 是通过 Arch Linux AUR 部署的。

编译安装yay
pacman -S --needed git base-devel
git clone https://aur.archlinux.org/yay.git
cd yay
makepkg -si
  • 使用 yay 安装 ocserv :

使用 yay 安装 ocserv
yay -S ocserv

Let's Encrpyt Certbot生成证书(推荐)

Arch linux官方提供了Let's Encrypt客户端Certbot,所以安装非常简单:

安装certbot
pacman -S certbot
  • 确保80端口没有使用中,然后从Let’s Encrypt获取一个TLS证书:

使用certbot获取letsencrypt证书
sudo certbot certonly --standalone --preferred-challenges http --agree-tos --email admin@example.com -d vpn.exapmle.com

备注

在执行上述 certbot 命令获取证书前,需要暂停80端口的nginx服务,因为 certbot 会监听该端口来迎接Let's Encrypt验证域名 vpn.example.com (也就是你执行生成证书的服务器): --preferred-challenges http ... -d vpn.exapmle.com

否则会报错:

暂停80端口nginx,否则使用certbot获取letsencrypt证书时报错
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator standalone, Installer None
Cert is due for renewal, auto-renewing...
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for vpn.example.com
Cleaning up challenges
Problem binding to port 80: Could not bind to IPv4 or IPv6.

一切正常的话,会收到如下正确获得并存储证书的信息:

使用certbot获取letsencrypt证书正确完成时输出信息
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator standalone, Installer None
Cert is due for renewal, auto-renewing...
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for vpn.example.com
Waiting for verification...
Cleaning up challenges

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 2023-04-14. 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"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

警告

Let's Encrypt提供的证书有效期3个月,所以每3个月需要使用上述命令重新生成一次证书。建议 使用cron定时更新letsencrypt证书

手工生成临时使用自签名证书(可选)

由于我暂时没有搞定域名注册,所以我也临时使用了以下命令生成证书(正式使用还是要采用上文Let's Encrypt证书):

使用 openssl 创建自签名证书
openssl req -x509 -nodes -newkey rsa:4096 -keyout /etc/ocserv/ocserv.key -out /etc/ocserv/ocserv.cert -days 3650 -subj "/CN=ocserv"

配置ocserv

Arch Linux AUR 提供的 ocserv 软件包提供了配置文件 /etc/ocserv.conf/etc/ocserv-passwd ,但是我发现实际上没有使用( systemctl start ocserv 显示使用 /etc/ocserv/ocserv.conf )

准备ocserv目录及配置文件
mkdir /etc/ocserv
mv /etc/ocserv.config /etc/ocserv/
rm /etc/ocserv-passwd

修订ocserv配置文件 /etc/ocserv/ocserv.config

  • 如果使用自签名证书,配置如下:

自签名证书配置
# 以下行注释关闭使用系统账号登陆
# auth = "pam[gid-min=1000]"

# 添加以下行表示独立密码文件认证
auth = "plain[passwd=/etc/ocserv/ocpasswd]"

# 只开启TCP端口,关闭UDP端口,以便使用BBR加速
tcp-port = 443
# udp-port = 443

# 修改证书,注释前两行,添加后两行,表示使用Let's Encrypt服务器证书
# server-cert = /etc/ssl/certs/ssl-cert-snakeoil.pem
# server-key = /etc/ssl/private/ssl-cert-snakeoil.key
server-cert = /etc/letsencrypt/live/vpn.example.com/fullchain.pem
server-key = /etc/letsencrypt/live/vpn.example.com/privkey.pem

# 设置客户端最大连接数量,默认是16
max-clients = 16
# 每个用户并发设备数量
max-same-clients = 2

# 启用MTU discovery以提高性能
#try-mtu-discovery = false
try-mtu-discovery = true

# 设置默认域名为vpn.example.com
default-domain = vpn.example.com

# 修改IPv4网段配置,注意不要和本地的IP网段重合
ipv4-network = 10.10.10.0

# 将所有DNS查询都通过VPN(防止受到DNS污染)
tunnel-all-dns = true

# 设置使用Google的公共DNS
dns = 8.8.8.8

# 注释掉所有路由参数
# route = 10.10.10.0/255.255.255.0
# route = 192.168.0.0/255.255.0.0
# route = fef4:db8:1000:1001::/64
# no-route = 192.168.5.0/255.255.255.0
  • 创建VPN账号:

启动ocserv

  • 启动ocserv:

使用systemd启动ocserv
systemctl start ocserv
systemctl enable ocserv

IP转发和MASQUERADE

  • 激活IP Forwarding (重要步骤)

修改 /etc/sysctl.conf 添加配置并激活:

激活IP Forwarding
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p
  • 配置防火墙的IP Masquerading (这里假设网卡接口是 ens3 )以及允许端口访问,配置好以后保存配置(以便后续通过 Systemd进程管理器 启动恢复 :

iptables激活IP MASQUERATE
# MASQUERADE
iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE
  
# 在防火墙上开启端口443
iptables -I INPUT -p tcp --dport 443 -j ACCEPT
iptables -I INPUT -p udp --dport 443 -j ACCEPT

# 保存防火墙配置
iptables-save > /etc/iptables.rules
  • 创建一个systemd服务来启动时恢复iptables规则,编辑 /etc/systemd/system/iptables-restore.service :

配置systemd服务在启动时恢复iptables规则
[Unit]
Description=Packet Filtering Framework
Before=network-pre.target
Wants=network-pre.target

[Service]
Type=oneshot
ExecStart=/sbin/iptables-restore /etc/iptables.rules
ExecReload=/sbin/iptables-restore /etc/iptables.rules
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
  • 激活 iptables-restore 服务:

激活systemd服务在启动时恢复iptables规则
systemctl daemon-reload
systemctl enable iptables-restore

错误排查

客户端连接错误排查

使用Cisco Secure Client连接认证后失败,检查日志:

使用 journalctl 检查ocserv
journalctl -u ocserv

日志显示找不到 tun 设备:

ocserv日志显示找不到tun设备
Apr 24 22:07:05 serv-arch ocserv[577235]: NLM query failed No such file or directory
Apr 24 22:07:05 serv-arch ocserv[577235]: main: delaying accepts for 100 ms
Apr 24 22:07:09 serv-arch ocserv[577237]: sec-mod: sec-mod instance 0 issue cookie
Apr 24 22:07:09 serv-arch ocserv[577237]: sec-mod: using 'plain' authentication to authenticate user (session: /bxaa+)
Apr 24 22:07:20 serv-arch ocserv[577235]: NLM query failed No such file or directory
Apr 24 22:07:20 serv-arch ocserv[577235]: main: delaying accepts for 100 ms
Apr 24 22:07:22 serv-arch ocserv[577235]: NLM query failed No such file or directory
Apr 24 22:07:22 serv-arch ocserv[577235]: main: delaying accepts for 100 ms
Apr 24 22:07:23 serv-arch ocserv[577235]: NLM query failed No such file or directory
Apr 24 22:07:23 serv-arch ocserv[577235]: main: delaying accepts for 100 ms
Apr 24 22:07:24 serv-arch ocserv[577235]: main[huatai]:1.80.209.3:5790 new user session
Apr 24 22:07:24 serv-arch ocserv[577237]: sec-mod: initiating session for user 'huatai' (session: /bxaa+)
Apr 24 22:07:24 serv-arch ocserv[577235]: main: tun.c:654: Can't open /dev/net/tun: No such device
Apr 24 22:07:24 serv-arch ocserv[577235]: main: tun.c:729: Can't open tun device: No such device
Apr 24 22:07:24 serv-arch ocserv[577235]: main[huatai]:1.80.209.3:5790 failed authentication attempt for user 'huatai'
Apr 24 22:07:24 serv-arch ocserv[577487]: worker: 1.80.209.3 failed cookie authentication attempt
Apr 24 22:07:24 serv-arch ocserv[577237]: sec-mod: temporarily closing session for huatai (session: /bxaa+)

但是实际上 /dev/net/tun 设备文件存在,我参考 OpenVPN 2.3.1 - ERROR: Cannot open TUN/TAP dev /dev/net/tun ,看起来是我最近做了一次系统升级,但是升级后没有重启系统导致的。重启系统之后,该问题消失

客户端连接后无法访问internet

我使用Cisco Secure Client连接VPN之后,发现无法访问Internet

  • 已经确认服务器开启了 net.ipv4.ip_forward = 1

乌龙了 忘记配置防火墙IP Masquerading,执行以下命令开启(上文步骤已经补充):

启用IP Masquerade
iptables -t nat -A POSTROUTING -o enp1s0 -j MASQUERADE

备注

实际需要参考上文,通过 Systemd进程管理器 自动回复防火墙IP Masquerading

参考