运行 OpenConnect VPN 服务器 & Apache/Nginx 与 HAProxy 在同一个盒子上

本教程将向您展示如何运行 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 地址。