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

  • 技术文档
  • 2022.03.24
  • 浏览:625

本教程将向您展示如何使用HAProxy在同一个设备上运行OpenConnect VPN服务器(ocserv)和Apache/Nginx。OpenConnect(ocserv)是Cisco AnyConnect VPN协议的开源实现。

ocserv-apache-nginx-haproxy

先决条件

为了学习本教程,假设您已经使用Let's Encrypt TLS server证书设置了OpenConnect VPN服务器。如果没有,请遵循以下教程之一。

  • 在Ubuntu20.04上用Let's Encrypt设置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配置

首先,编辑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地址。保存并关闭文件。然后重启ocserv。

sudo systemctl restart ocserv

接下来,我们还需要让web服务器只监听本地主机,而不是监听公共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占用。保存并关闭文件。Nginx主配置文件/etc/Nginx/Nginx。conf和默认的server block/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

森托斯/瑞尔

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占用。保存并关闭文件。

然后编辑/etc/apache2/端口。Debian/Ubuntu上的conf文件。

sudo nano /etc/apache2/ports.conf

编辑/etc/httpd/conf.d/ssl。CentOS/RHEL上的conf文件。

sudo nano /etc/httpd/conf.d/ssl.conf

改变

Listen 443

Listen 127.0.0.2:443

保存并关闭文件。重启Apache。

sudo systemctl restart apache2

sudo systemctl restart httpd

单倍体构型

现在安装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。实例com和ocserv和www.example使用的域名。com与您的web服务器使用的域名。

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。实例com和ocserv和www.example使用的域名。com与您的web服务器使用的域名。

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

保存并关闭文件。然后重启HAproxy。

sudo systemctl restart haproxy

在上面的配置中,我们利用TLS中的SNI(服务器名称指示)功能来区分VPN流量和正常HTTPS流量。

  • 当vpn。实例com位于TLS客户端Hello,HAProxy将流量重定向到ocserv后端。
  • 当www.example。com位于TLS客户端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应用程序不支持TLS SNI,因此最好在HAProxy配置文件中将ocserv设置为默认后端。

为您的网站续订Let's Encrypt证书时,建议您使用http-01质询而不是tls-alpn-01质询,因为HAproxy正在侦听公共IP地址的端口443,因此它可能会干扰续订过程。

sudo certbot renew --preferred-challenges http-01

修正单极性误差

如果您的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;     .... }

保存并关闭文件。然后重新启动Nginx。

sudo systemctl restart nginx

此外,考虑删除HAproxy后端服务器的健康检查。所以改变

server nginx 127.0.0.2:443 check

server nginx 127.0.0.2:443

保存并关闭文件。然后重启HAproxy。

sudo systemctl restart haproxy

如何使用HAProxy在ocserv中启用IPv6

首先,为vpn创建AAAA记录。实例因此,当您在ocserv中完成IPv6设置后,DNS记录应该传播到Internet。

测试IPv6连接

要在IPv6协议中建立VPN隧道,请确保VPN服务器具有公共IPv6地址。(VPN客户端不必具有公共IPv6地址。)要找到答案,请运行以下命令。

ip addr

找到主网络接口。如果你能找到iNet 6。。。。范围全局行,如下图所示,则您有一个公共IPv6地址。带有作用域链接的inet6地址是专用IPv6地址。

ip addr ipv6 scope global

那就去https://test-ipv6.com/检查你的IPv6连接。如果VPN客户端有一个公共IPv6地址,它可能会告诉您,您的VPN只保护一个协议,而不是两个协议。那是因为我们没有在ocserv中启用IPv6。

your VPN is only protecting one protocol, not both

在ocserv中启用IPv6

要在ocserv中启用IPv6,请编辑ocserv配置文件。

sudo nano /etc/ocserv/ocserv.conf

找到下面两行并取消注释,这样VPN客户端将获得专用IPv6地址。

ipv6-network = fda9:4efe:7e3b:03ea::/48 ipv6-subnet-prefix = 64

如果你看到下面这行

ipv6-network = fda9:4efe:7e3b:03ea::/64

请将其更改为:

ipv6-network = fda9:4efe:7e3b:03ea::/48

保存并关闭文件。重新启动ocserv以使更改生效。

sudo systemctl restart ocserv

为IPv6启用IP转发

然后我们需要在Linux内核中为IPv6启用IP转发。编辑sysctl。conf文件。

sudo nano /etc/sysctl.conf

在该文件末尾添加以下行。

net.ipv6.conf.all.forwarding=1

保存并关闭文件。然后使用下面的命令应用更改。

sudo sysctl -p

在防火墙中设置IPv6(Debian、Ubuntu)

接下来,我们需要在UFW防火墙中设置IPv6伪装,以便服务器成为VPN客户端的虚拟路由器。

sudo nano /etc/ufw/before6.rules

默认情况下,过滤器表有一些规则。在该文件末尾添加以下行。将ens3替换为您自己的网络接口名称。在Nano文本编辑器中,按Ctrl+W,然后按Ctrl+V,可以转到文件的末尾。

# NAT table rules *nat :POSTROUTING ACCEPT [0:0] -A POSTROUTING -o ens3 -j MASQUERADE  # End each table with the 'COMMIT' line or these rules won't be processed COMMIT

IPv6 masquerading in the UFW firewall

默认情况下,UFW禁止数据包转发。我们可以允许我们的专用IPv6网络进行转发。在该文件中找到ufw6 before forward链,并添加以下3行,如果源IP或目标IP在fda9:4efe:7e3b:03ea::/48范围内,这3行将接受数据包转发。

# allow forwarding for VPN -A ufw6-before-forward -s fda9:4efe:7e3b:03ea::/48 -j ACCEPT -A ufw6-before-forward -d fda9:4efe:7e3b:03ea::/48 -j ACCEPT

ufw allow packet forwarding for ipv6 network

保存并关闭文件。我们还需要在防火墙的输入链中允许IPv6 VPN客户端。

sudo ufw allow in from fda9:4efe:7e3b:03ea::/48

重新启动UFW以使更改生效。

sudo systemctl restart ufw

现在,如果使用以下命令列出NAT表的后路由链中的规则:

sudo ip6tables -t nat -L POSTROUTING

你可以看到化装规则。

enable ipv6 in ocserv openconnect vpn

断开当前VPN连接,为VPN添加AAAA记录。实例com并重新建立VPN连接。那就去https://test-ipv6.com/检查你的IPv6连接。

在防火墙中设置IPv6(CentOS)

为IPv6启用伪装。

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv6" source address="fda9:4efe:7e3b:03ea::/48" masquerade'

允许在输入链中使用VPN客户端。

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv6" source address="fda9:4efe:7e3b:03ea::/48" accept'

重新加载firewalld以使更改生效。

sudo systemctl reload firewalld

在绑定解析器中配置IPv6

如果在VPN服务器上运行自己的绑定DNS解析器,可以在/etc/ocserv/ocserv中添加以下行。conf文件,将VPN服务器设置为VPN客户端的DNS解析程序。

dns = fda9:4efe:7e3b::1

保存并关闭文件。要在IPv6中查询DNS名称,我们需要将BIND配置为允许IPv6 VPN客户端。

Debian/Ubuntu

sudo nano /etc/bind/named.conf.options

找到allow recursion参数并将其更改为:

allow-recursion { 127.0.0.1; 10.10.10.0/24; fda9:4efe:7e3b:03ea::/48; };

保存并关闭文件。重新启动BIND9。

sudo systemctl restart bind9

森托斯

sudo nano /etc/named.conf

找到allow query参数并将其更改为:

allow-query { 127.0.0.1; 10.10.10.0/24; fda9:4efe:7e3b:03ea::/48; };

保存并关闭文件。重新启动BIND9。

sudo systemctl restart named

在HAProxy中设置IPv6

编辑HAProxy配置文件。

sudo nano /etc/haproxy/haproxy.cfg

让https前端同时监听IPv4和IPv6地址。显然,您需要使用自己服务器的公共IPv6地址。

frontend https    bind 12.34.56.78:443    bind 2607:f8b0:4006:810::200e:443    mode tcp    tcp-request inspect-delay 5s    tcp-request content accept if { req_ssl_hello_type 1 }

然后找到ocserv后端并添加IPv6服务器。

backend ocserv    mode tcp    option ssl-hello-chk    server ocserv 127.0.0.1:443 send-proxy-v2    server ocserv6 [::1]:443 send-proxy-v2

保存并关闭文件。

要使ocserv同时在127.0.0.1和::1上侦听,请编辑/etc/hosts文件。

sudo nano /etc/hosts

编辑127.0.0.1和::1的条目,如下所示,这样vpn。实例com主机名可以解析为两个地址。

127.0.0.1   localhost vpn.example.com  ::1         ip6-localhost ip6-loopback vpn.example.com

保存并关闭文件。然后编辑ocserv配置文件。

sudo nano /etc/ocserv/ocserv.conf

找到下面这行。

listen-host = 127.0.0.1

换成

listen-host  = vpn.example.com

ocserv将查找vpn的IPv4和IPv6地址。实例com,并绑定到127.0.0.1和::1地址。保存并关闭文件。然后重启ocserv和HAProxy

sudo systemctl restart ocserv sudo systemctl restart haproxy

现在运行以下命令来检查ocserv的监听状态。您将看到它同时在127.0.0.1和::1上收听。

sudo ss -lnpt | grep ocserv

sudo ss -lnpt | grep ocserv ipv6

测试IPv6连接

重新启动VPN客户端并转到https://test-ipv6.com/检查你的IPv6连接。如果一切顺利,您应该在测试结果中看到VPN服务器的IPv4和IPv6地址。“VPN只保护一个协议”的警告应该消失。

Testing IPv6 Connectivity ocserv VPN

如果在测试结果中看不到VPN服务器的IPv6地址,可能需要重新启动VPN客户端并重新建立VPN连接。

注意:VPN客户端不必具有公共IPv6地址。它可以通过IPv4 VPN隧道使用IPv6。

收尾

我希望本教程能帮助您在同一台机器上运行OpenConnect VPN服务器和Apache/Nginx。和往常一样,如果你觉得这篇文章很有用,那么订阅我们的免费时事通讯以获得更多提示和窍门。当心?