OpenConnect 是一个用于连接到虚拟专用网络 (VPN) 的开源软件应用程序,它实现了安全的点对点连接。它最初是作为 Cisco 专有 AnyConnect SSL VPN 客户端的开源替代品编写的。而 ocserv 是 OpenConnect 的服务端。由于很多厂商需要用 Cisco 的 AnyConnect VPN 在海外开展业务,所以虽然 OpenConnect 的流量特征明显,但是受影响却很小。

准备系统

net.ipv4.neigh.default.base_reachable_time_ms = 600000
net.ipv4.neigh.default.mcast_solicit = 20
net.ipv4.neigh.default.retrans_time_ms = 250
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.eth0.rp_filter=0
net.ipv4.conf.eth1.rp_filter=0
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
net.ipv4.tcp_fastopen=3
net.ipv4.ip_forward=1

# disable ping echo
# net.ipv4.icmp_echo_ignore_all=1

安装 certbot,配置证书

openconnect 需要用 ssl 证书来加密连接,所以需要用 Let’s Encrypt 来获取一个证书。

$ sudo apt update
$ sudo apt install certbot

先将域名做 dns 设置,指向 vps,然后签发一个 tls 证书:

$ certbot certonly --standalone --preferred-challenges http --agree-tos --email you@gmail.com -d vpn.example.com

安装 ocserv:

$ sudo apt install ocserv

配置 ocserv.conf:

server-cert = /etc/letsencrypt/live/vpn.example.com/fullchain.pem
server-key = /etc/letsencrypt/live/vpn.example.com/privkey.pem

客户端证书认证

ocserv 支持多种客户端登陆认证方式,这里我们用证书登陆。 设置客户端证书:

$ apt install gnutls-bin
$ mkdir /etc/ocserv/ssl/
$ cd /etc/ocserv/ssl

创建 CA 的私钥:

$ certtool --generate-privkey --outfile ca-privkey.pem

新建 CA 证书的模板

$ vim ca-cert.cfg
# X.509 Certificate options

# The organization of the subject.
organization = "vpn.example.com"

# The common name of the certificate owner.
cn = "Example CA"

# The serial number of the certificate.
serial = 001

# In how many days, counting from today, this certificate will expire. Use -1 if there is no expiration date.
expiration_days = -1

# Whether this is a CA certificate or not
ca

# Whether this certificate will be used to sign data
signing_key

# Whether this key will be used to sign other certificates.
cert_signing_key

# Whether this key will be used to sign CRLs.
crl_signing_key

生成 CA 证书:

$ certtool --generate-self-signed --load-privkey ca-privkey.pem --template ca-cert.cfg --outfile ca-cert.pem

创建客户端密钥

$ certtool --generate-privkey --outfile client-privkey.pem

客户端证书模板

$ vim client-cert.cfg
# X.509 Certificate options
# The organization of the subject.
organization = "vpn.example.com"

# The common name of the certificate owner.
cn = "John Doe"

# A user id of the certificate owner.
uid = "username"

# In how many days, counting from today, this certificate will expire. Use -1 if there is no expiration date.
expiration_days = 3650

# Whether this certificate will be used for a TLS server
tls_www_client

# Whether this certificate will be used to sign data
signing_key

# Whether this certificate will be used to encrypt data (needed
# in TLS RSA ciphersuites). Note that it is preferred to use different
# keys for encryption and signing.
encryption_key

用 CA 密钥签发一个客户端证书

$ certtool --generate-certificate --load-privkey client-privkey.pem --load-ca-certificate ca-cert.pem --load-ca-privkey ca-privkey.pem --template client-cert.cfg --outfile client-cert.pem

组合客户端私钥和证书为一个 PKCS #12 文件

$ certtool --to-p12 --load-privkey client-privkey.pem --load-certificate client-cert.pem --pkcs-cipher aes-256 --outfile client.p12 --outder

ios 客户端不支持 aes-256 算法,需要用 3des-pkcs12 算法

$ certtool --to-p12 --load-privkey client-privkey.pem --load-certificate client-cert.pem --pkcs-cipher 3des-pkcs12 --outfile ios-client.p12 --outder

开启证书登陆

$ vim /etc/ocserv/ocserv.conf
auth = "certificate"
ca-cert = /etc/ocserv/ssl/ca-cert.pem

$ systemctl restart ocserv

剩余的配置

keepalive = 30
try-mtu-discovery = true
default-domain = vpn.example.com
ipv4-network = 10.10.10.0
tunnel-all-dns = true
# 注释下面的所有
#route = 10.0.0.0/8
#route = 172.16.0.0/12
#route = 192.168.0.0/16
#route = fd00::/8
#route = default
#no-route = 192.168.5.0/255.255.255.0

开启 ip 转发
net.ipv4.ip_forward = 1

iptables,源地址转换:
$ iptables -t nat -I POSTROUTING -s 10.10.10.0/24 -o eth0 -j MASQUERADE

自动更新 Let’s Encrypt 证书

crontab -e
@daily certbot renew --quiet && systemctl restart ocserv

关闭 tls1.0 和 tls1.1

这两个版本的 tls 已经很老了,现在最新的都会用1.2或者1.3版本

tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-RSA:-VERS-SSL3.0:-ARCFOUR-128"
改为
tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-RSA:-VERS-SSL3.0:-ARCFOUR-128:-VERS-TLS1.0:-VERS-TLS1.1"
检测
openssl s_client -connect vpn.your-domain.com:443 -tls1
openssl s_client -connect vpn.your-domain.com:443 -tls1.1

客户端配置

linux 可以用 NetworkManager 管理 openconnect 客户端连接,需要安装networkmanager-openconnect这个包。ios 设备将 p12 文件发送到设备上,然后用 AnyConnect 打开,导入证书。

报错

Read error on DTLS session: The transmitted packet is too large (EMSGSIZE).

需要设置一个小的 mtu 值:
ocserv.conf -> mtu = 1466