OpenConnect VPN

Kubernetes是Google开发的容器编排系统,当你开始学习和测试Kubernetes时候,你会发现,很多基于Google的软件仓库无法访问,会给你的学习和工作带来极大的困扰。

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

备注

如果你使用Google的软件仓库来安装最新版本的Kubernetes,并按照Google的文档进行实践,需要一个科学上网的梯子来帮助你。

  • 准备工作:

    • 需要购买一台海外VPS

    • 需要一个域名,在注册域名中添加一条记录指向新购买VPS的IP地址

备注

使用Let’s Encrypt签发的证书是针对域名的,所以不仅需要购买VPS,还需要提前准备好域名。

部署OpenConnect VPN Server

备注

本文方案实践服务器和客户端都基于Ubuntu 18.10系统,其他发行版可能会有一些差异。

详细操作步骤参考 在Ubuntu部署OpenConnect VPN服务器

  • 安装ocserv:

    apt install ocserv
    

安装完成后,OpenConnect VPN服务自动启动,可以通过 systemctl status ocserv 检查。

源代码编译

Arch Linux 上安装 ocserv 比较麻烦一些,需要通过 Arch Linux AUR 编译安装,也就是先安装 yay ,然后执行 yay -S ocserv ,不过在2023年11月的尝试安装失败,原因是 ocserv 依赖的 freeradius-client (AUR) 引用了一个已经不再维护的 libpcl ,所以直接安装会失败。所以我实际安装是通过源代码编译安装的:

源代码编译安装 ocserv
wget https://gitlab.com/openconnect/ocserv/-/archive/1.2.2/ocserv-1.2.2.tar.gz
tar xfz ocserv-1.2.2.tar.gz
cd ocserv-1.2.2
./autogen.sh
./configure && make && sudo make install
# 方便采用ubuntu的启动配置文件
ln -s /usr/local/sbin/ocserv /usr/sbin/ocserv

服务起停配置文件我采用了 Ubuntu Linux 提供的 Systemd进程管理器 配置:

  • /lib/systemd/system/ocserv.service :

Ubuntu Linux 提供的ocserv启动systemd配置
[Unit]
Description=OpenConnect SSL VPN server
Documentation=man:ocserv(8)
After=network-online.target
After=dbus.service

[Service]
PrivateTmp=true
PIDFile=/run/ocserv.pid
ExecStart=/usr/sbin/ocserv --foreground --pid-file /run/ocserv.pid --config /etc/ocserv/ocserv.conf
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target
  • 创建 /etc/ocserv/ocserv.conf (从 Ubuntu Linux 发行版默认配置提取):

Ubuntu Linux 提供的ocserv默认配置
# 以下行注释关闭使用系统账号登陆
# 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

备注

Arch Linux 仓库提供软件包上述配置文件可能已经就绪(不过我尚未实践 Arch Linux AUR 安装成功)

配置

  • 安装Let’s Encrypt客户端(Certbot):

    sudo apt install software-properties-common
    sudo add-apt-repository ppa:certbot/certbot
    sudo apt update
    sudo apt install certbot
    

警告

本文使用了 example.com 作为案例域名,实际操作时,请使用你自己的正确域名

  • 确保80端口nginx服务暂停,然后从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证书

  • 修改ocserv配置文件 /etc/ocserv/ocserv.conf :

配置 /etc/ocserv/ocserv.conf 添加Let’s Encrypt证书
# 以下行注释关闭使用系统账号登陆
# 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
  • 重启ocserv:

    sudo systemctl restart ocserv
    
  • 创建VPN账号:

    sudo ocpasswd -c /etc/ocserv/ocpasswd username
    
  • 激活IP Forwarding (重要步骤)

修改 /etc/sysctl.conf 添加:

net.ipv4.ip_forward = 1

然后执行:

sudo sysctl -p
  • 配置防火墙的IP Masquerading (这里加设网卡接口是 ens3

    sudo iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE
    
  • 在防火墙上开启端口443:

    sudo iptables -I INPUT -p tcp --dport 443 -j ACCEPT
    sudo iptables -I INPUT -p udp --dport 443 -j ACCEPT
    

保存防火墙配置:

sudo iptables-save > /etc/iptables.rules
  • 创建一个systemd服务来启动时恢复iptables规则,编辑 /etc/systemd/system/iptables-restore.service

    [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 服务:

    sudo systemctl daemon-reload
    sudo systemctl enable iptables-restore
    

使用OpenConnect VPN Client

备注

详细操作步骤参考 使用OpenConnect客户端

  • 安装 OpenConnect:

    sudo apt install openconnect
    
  • 连接服务器:

    sudo openconnect <VPN服务器域名>
    

连接建立以后,就可以正常使用apt安装Google软件仓库中的软件。

OpenConnect VPN Client使用技巧

我在使用 openconnect 遇到以下几个问题需要解决:

  • 需要手工输入用户名和密码

  • 服务器证书过期后每次都要手工接受服务器证书 (早期版本可以使用 --no-cert-check 参数绕过,但是现在不行,必须明确接受服务器证书)

openconnect 提供了 -u 参数传递账号名,但是使用 -p 参数传递密码失败,所以改成 --passwd-on-stdin 从管道获取密码:

openconnect从标准输入(管道)获取登陆密码
echo mypassword | sudo openconnect -u <myusernae> --passwd-on-stdin <vpnserver>:<vpn_port>

对于接受服务器证书的问题,可以使用 --servercert 参数来传递服务器证书,这样就不需要每次都手工确认一次了,所以完整的命令

openconnect从标准输入(管道)获取登陆密码并且使用服务器证书
echo mypassword | sudo openconnect -u <myusernae> --passwd-on-stdin --serverceet <cert> <vpnserver>:<vpn_port>

Cisco AnyConnect VPN Client

Cisco AnyConnnect VPN Client 和OpenConnect VPN Server (ocserv) 兼容,所以可以从Cisco官方网站下载客户端。

修改ocserv端口

如果在同一台主机上部署ocserv vpn server和WEB服务,例如 部署Ghost CMS ,则会遇到端口冲突问题:因为VPN也同样使用了https端口443。

解决的方法是:调整VPN Server的端口,修订成 404 端口:

  • 修改 /etc/ocserv/ocserv.conf

    tcp-port = 404
    udp-port = 404
    
  • 修改 /lib/systemd/system/ocserv.socket Socket端口对应监听:

    [Socket]
    ListenStream=404
    ListenDatagram=404
    
  • 重新加载配置:

    systemctl reload-daemon
    
  • 重启服务:

    systemctl restart ocserv
    

参考