本教程将向您展示如何运行 OpenConnect VPN 服务器 (ocserv) 和 Apache/Nginx 与 HAProxy 在同一个盒子上。 OpenConnect (ocserv) 是 Cisco AnyConnect VPN 协议的开源实现。
先决条件
要学习本教程,假设您已经使用 Let’s Encrypt TLS 服务器证书设置了 OpenConnect VPN 服务器。 如果没有,请按照以下教程之一进行操作。
- 使用 Let’s Encrypt 在 Ubuntu 20.04 上设置 OpenConnect VPN 服务器 (ocserv)
- 使用 Let’s Encrypt 在 Ubuntu 16.04/18.04 上设置 OpenConnect VPN 服务器 (ocserv)
- 使用 Let’s Encrypt 在 Debian 10 Buster 上设置 OpenConnect VPN 服务器 (ocserv)
- 使用 Let’s Encrypt 在 CentOS 8/RHEL 8 上设置 OpenConnect VPN 服务器 (ocserv)
使 OpenConnect VPN 服务器和 Web 服务器同时使用端口 443
默认情况下,OpenConnect VPN 服务器侦听端口 443。如果您已经有 Apache/Nginx监听443端口,那么ocserv无法绑定443端口。 可以配置ocserv监听另一个端口,但是需要终端用户在客户端软件中指定端口,如果你关心的话应该避免用户体验。 此外,TCP 端口 443 上的 TLS 流量通常在 QoS(服务质量)中享有更高的优先级,因此您将获得更好的速度。
通常一个端口只能被一个进程使用。 但是,我们可以使用 HAproxy(高可用性代理)和 SNI(服务器名称指示)来使 ocserv 和 Apache/Nginx 同时使用端口 443。
观察配置
首先,编辑ocserv配置文件。
sudo nano /etc/ocserv/ocserv.conf
取消注释以下行。 这将允许 ocserv 获取客户端 IP 地址而不是 HAproxy IP 地址。
listen-proxy-proto = true
然后找到以下行。
#listen-host = [IP|HOSTNAME]
将其更改为
listen-host = 127.0.0.1
这将使 ocserv 侦听 127.0.0.1,因为稍后 HAproxy 将需要侦听公共 IP 地址。 Save 和 close 文件。 然后重启ocserv。
sudo systemctl restart ocserv
接下来,我们还需要让 web 服务器只监听 localhost,而不是监听公共 IP 地址。
Nginx 配置
如果您使用 Nginx,请编辑服务器块文件。
sudo nano /etc/nginx/conf.d/example.com.conf
在 SSL 服务器块中,找到以下指令。
listen 443 ssl;
将其更改为
listen 127.0.0.2:443 ssl;
这次我们让它听 127.0.0.2:443
因为 127.0.0.1:443
已经被 ocserv 占用了。 Save 和 close 文件。 Nginx 主配置文件 /etc/nginx/nginx.conf
和默认的服务器块 /etc/nginx/sites-enabled/default
可能包含一个监听 443 的默认虚拟主机,因此您可能也需要编辑此文件。
然后重启Nginx。
sudo systemctl restart nginx
Apache 配置
如果你使用 Apache Web 服务器,编辑您的虚拟主机文件。
Debian/Ubuntu
sudo nano /etc/apache2/sites-enabled/example.com.conf
CentOS/RHEL
sudo nano /etc/httpd/conf.d/example.com.conf
在 SSL 虚拟主机中,更改
<VirtualHost *:443>
到
<VirtualHost 127.0.0.2:443>
这次我们让它听 127.0.0.2:443
因为 127.0.0.1:443
已经被 ocserv 占用了。 Save 和 close 文件。
然后编辑 /etc/apache2/ports.conf
Debian/Ubuntu 上的文件。
sudo nano /etc/apache2/ports.conf
编辑/etc/httpd/conf.d/ssl.conf
CentOS/RHEL 上的文件。
sudo nano /etc/httpd/conf.d/ssl.conf
改变
Listen 443
到
Listen 127.0.0.2:443
Save 和 close 文件。 重新开始 Apache.
sudo systemctl restart apache2
或者
sudo systemctl restart httpd
HAProxy 配置
现在安装HAproxy。
sudo apt install haproxy
或者
sudo dnf install haproxy
启动HAProxy
sudo systemctl start haproxy
编辑配置文件。
sudo nano /etc/haproxy/haproxy.cfg
如果您使用 Nginx,请将以下几行复制并粘贴到文件末尾。 代替 12.34.56.78
使用您服务器的公共 IP 地址。 代替 vpn.example.com
使用 ocserv 使用的域名和 www.example.com
与您的网络服务器使用的域名。
frontend https bind 12.34.56.78:443 mode tcp tcp-request inspect-delay 5s tcp-request content accept if { req_ssl_hello_type 1 } use_backend ocserv if { req_ssl_sni -i vpn.example.com } use_backend nginx if { req_ssl_sni -i www.example.com } use_backend nginx if { req_ssl_sni -i example.com } default_backend ocserv backend ocserv mode tcp option ssl-hello-chk # pass requests to 127.0.0.1:443. Proxy protocol (v2) header is required by ocserv. server ocserv 127.0.0.1:443 send-proxy-v2 backend nginx mode tcp option ssl-hello-chk server nginx 127.0.0.2:443 check
如果你使用 Apache, 将以下行复制并粘贴到文件末尾。 代替 12.34.56.78
使用您服务器的公共 IP 地址。 代替 vpn.example.com
使用 ocserv 使用的域名和 www.example.com
与您的网络服务器使用的域名。
frontend https bind 12.34.56.78:443 mode tcp tcp-request inspect-delay 5s tcp-request content accept if { req_ssl_hello_type 1 } use_backend ocserv if { req_ssl_sni -i vpn.example.com } use_backend apache if { req_ssl_sni -i www.example.com } use_backend apache if { req_ssl_sni -i example.com } default_backend ocserv backend ocserv mode tcp option ssl-hello-chk # pass requests to 127.0.0.1:443. Proxy protocol (v2) header is required by ocserv. server ocserv 127.0.0.1:443 send-proxy-v2 backend apache mode tcp option ssl-hello-chk server apache 127.0.0.2:443 check
Save 和 close 文件。 然后重启HAproxy。
sudo systemctl restart haproxy
在上面的配置中,我们利用了 TLS 中的 SNI(服务器名称指示)功能来区分 VPN 流量和正常的 HTTPS 流量。
- 什么时候
vpn.example.com
在 TLS Client Hello 中,HAProxy 将流量重定向到 ocserv 后端。 - 什么时候
www.example.com
在 TLS Client Hello 中,HAProxy 将流量重定向到 apache/nginx 后端。 - 如果客户端未在 TLS Client Hello 中指定服务器名称,则 HAproxy 将使用默认后端 (ocserv)。
您可以使用 openssl
工具。 首先,多次运行以下命令。
echo | openssl s_client -connect your-server-IP:443 | grep subject
我们在上面的命令中没有指定服务器名称,所以HAproxy会一直将请求传递给默认的后端(ocserv),它的证书会发送给客户端。 接下来,运行以下两个命令。
echo | openssl s_client -servername www.example.com -connect your-server-IP:443 | grep subject echo | openssl s_client -servername vpn.example.com -connect your-server-IP:443 | grep subject
现在我们在命令中指定了服务器名称,因此 HAproxy 将根据我们定义的 SNI 规则传递请求。 请注意,Cisco AnyConnect App 不支持 TLS SNI,因此最好设置 ocserv
作为 HAProxy 配置文件中的默认后端。
为您的网站更新 Let’s Encrypt 证书时,建议您使用 http-01
挑战而不是 tls-alpn-01
挑战,因为 HAproxy 正在侦听公共 IP 地址的端口 443,因此它会干扰更新过程。
sudo certbot renew --preferred-challenges http-01
修复 HAproxy 错误
如果你的 Apache/Nginx 网站没有显示在您的浏览器中,您会在 haproxy 日志中看到以下消息(/var/log/haproxy.log
)
Server nginx/nginx is DOWN, reason: Socket error, info: "Connection reset by peer backend nginx has no server available! Layer6 invalid response
可能是您的后端 Nginx Web 服务器正在使用带有 OCSP 必须装订扩展的 TLS 证书。 Nginx 不会在第一个 HTTP 请求上发送 OCSP 主食信息。 要使其工作,请确保在您的 Nginx 虚拟主机配置中添加一个解析器,如下所示。
{ .... ssl_trusted_certificate /etc/letsencrypt/live/www.example.com/chain.pem; ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8; .... }
Save 和 close 文件。 然后重启Nginx。
sudo systemctl restart nginx
此外,请考虑在 HAproxy 中删除后端服务器的健康检查。 所以改变
server nginx 127.0.0.2:443 check
到
server nginx 127.0.0.2:443
Save 和 close 文件。 然后重启HAproxy。
sudo systemctl restart haproxy
如何使用 HAProxy 在 ocserv 中启用 IPv6
首先,创建AAAA记录 vpn.example.com
在您的 DNS 区域编辑器中,所以当您在 ocserv 中完成 IPv6 设置时,DNS 记录应该传播到 Internet。
测试 IPv6 连接
要在 IPv6 协议中建立 VPN 隧道,请确保 VPN 服务器具有公共 IPv6 地址。 (VPN 客户端不必具有公共 IPv6 地址。)要查找,请运行以下命令。
ip addr
找到主网络接口。 如果你能找到一个 inet6 .... scope global
像下面这样的行,那么您就有了一个公共 IPv6 地址。 这 inet6
地址与 scope link
是私有 IPv6 地址。