使用 HAProxy 分流 443 端口上的多个服务
由于众所周知的原因,我们可能会希望在一台 VPS 的 443 端口上部署多个服务。当然,对有钱人来说,尽可以买多台 VPS 或者多个 IP。而对我等穷逼来说,只能想办法在一台 VPS 上分流了。最近经我不断折腾研究终于摸索出了方法,分流的服务为 SSH、ocserv、nginx 和 shadowsocks-libev。以下描述均基于 Ubuntu Server 16.04。
ocserv 的搭建
关于 ocserv,网上文章很多,所以我就不详细说了。但这里提一些大多数文章没有提到的要点。
为了使用 HAProxy 来反向代理 ocserv,你需要在 /etc/ocserv/ocserv.conf
中把端口改成一个 443 以外的端口:
tcp-port = 8443
udp-port = 8443
此外,为了使用非 443 的 UDP 端口,我直接把 ocserv.socket
这个服务给禁用了:
sudo systemctl disable ocserv.socket
然后修改 /lib/systemd/system/ocserv.service
,把 ocserv.service
改为独立启动,不依赖 ocserv.socket
:
# 注释掉这两行
#Requires=ocserv.socket
#Also=ocserv.socket
另外,为了让 HAProxy 可以根据 SNI 来转发,你需要给 ocserv 单独安排一个子域名,因此也需要给子域名单独申请证书。比如我使用的是 Let’s Encrypt,那么,就要如此修改 /etc/ocserv/ocserv.conf
:
server-cert = /etc/letsencrypt/live/ocserv.example.com/fullchain.pem
server-key = /etc/letsencrypt/live/ocserv.example.com/privkey.pem
使用 HAProxy V2 版本的代理协议,可以使 ocserv 可以识别连接的 IP:
listen-proxy-proto = true
另外在 Ubuntu 16.04 上有个配置如果不改就会出现 GnuTls 错误的日志:
isolate-workers = false
还有些配置不改的话我就连不上:
keepalive = 32400
cert-user-oid = 2.5.4.3
auth-timeout = 180
min-reauth-time = 300
cookie-timeout = 86400
cookie-rekey-time = 14400
deny-roaming = false
rekey-time = 172800
cisco-client-compat = true
然后就是一些优化:
dpd = 30
mobile-dpd = 90
try-mtu-discovery = true
compression = true
至于证书认证之类的,请参考其他文章吧,不详细讲了。
Nginx 配置
当然,还是要绑定到一个其他的端口上去:
listen 7443 ssl;
listen [::]:7443 ssl;
shadowsocks-libev 配置
需要启用 TLS 混淆,并设置 obfs-host
,绑定在非 443 端口上:
{
"server":["::0", "0.0.0.0"],
"server_port":8388,
"local_port":1080,
"password":"change_to_your_password",
"timeout":60,
"method":"chacha20-ietf-poly1305",
"fast_open": true,
"mode": "tcp_only",
"plugin": "obfs-server",
"plugin_opts": "obfs=tls;obfs-host=cloudfront.net;failover=cloudfront.net;fast-open"
}
另外,为了让客户端可以使用 443 端口进行 UDP 转发,可以启动两个 ss-server
。先新建一个配置文件:
sudo cp /etc/shadowsocks-libev/config.json /etc/shadowsocks-libev/udp.json
修改配置文件:
{
"server":["::0", "0.0.0.0"],
"server_port":443,
"local_port":1080,
"password":"change_to_your_password",
"timeout":60,
"method":"chacha20-ietf-poly1305",
"fast_open": true,
"mode": "udp_only"
}
新建一个 systemd 服务:
sudo systemctl enable [email protected]
sudo systemctl start [email protected]
HAProxy 配置
/etc/haproxy/haproxy.cfg
:
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
user haproxy
group haproxy
daemon
defaults
log global
mode tcp
option tcplog
option dontlognull
#maxconn 2000
timeout connect 24h
timeout client 24h
timeout server 24h
frontend ssl
mode tcp
bind *:443
tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }
acl ssh_payload payload(0,7) -m bin 5353482d322e30
acl vpn-app req_ssl_sni -i ocserv.example.com
acl web-app req_ssl_sni -i example.com
acl web-app req_ssl_sni -i www.example.com
acl shadowsocks-app req_ssl_sni -i cloudfront.net
use_backend ocserv if vpn-app
use_backend nginx if web-app
use_backend shadowsocks if shadowsocks-app
use_backend ocserv if { req.ssl_hello_type 1 } !vpn-app !web-app !shadowsocks-app
use_backend openssh if ssh_payload
use_backend openssh if !{ req.ssl_hello_type 1 } { req.len 0 }
backend openssh
mode tcp
server openssh 127.0.0.1:22
backend ocserv
mode tcp
option ssl-hello-chk
server server-vpn 127.0.0.1:8443 send-proxy-v2
backend shadowsocks
mode tcp
server shadowsocks 127.0.0.1:8388
backend nginx
mode tcp
option ssl-hello-chk
server server-web 127.0.0.1:7443
说明:
- 用 payload 的前 8 个字节判断为 SSH 协议
- 其他的使用 SNI 来判断
防火墙配置
只需要打开 443、443/udp、8443/udp 即可:
sudo ufw allow 443
sudo ufw allow 443/udp
sudo ufw allow 8443/udp
当然,为了使用 ocserv,还需要打开转发:
/etc/sysctl.conf
:
net.ipv4.ip_forward=1
net.ipv6.conf.default.forwarding=1
net.ipv6.conf.all.forwarding=1
/etc/ufw/sysctl.conf
net/ipv4/ip_forward=1
net/ipv6/conf/default/forwarding=1
net/ipv6/conf/all/forwarding=1
在 /etc/ufw/before.rules
最后加:
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 10.0.0.0/24 -o ens3 -j MASQUERADE
COMMIT
其中 -s
后面的 IP 段要跟 /etc/ocserv/ocserv.conf
中的 ipv4-network
和 ipv4-netmask
配置一致。