VLESS 与多域名 Nginx 站点共存的 SNI 分流配置方案
Doveccl
本文距离上次更新已过去 0 天,部分内容可能已经过时,请注意甄别。

最近开始白嫖公司的香港云服务器了,与之前自购的美国 VPS 对比测试下来无论是在网络延时、稳定性还是性能都有了比较大的改善。秉承资源利用最大化的原则,这台服务器的流量不能仅用于访问 Google,所以干脆把之前托管在 Github Pages 上的博客(其实是 Student Pack 到期之后,免费用户的私有项目用不了 GitHub Pages),以及自己搞的一些奇奇怪怪的网站也都迁移了过来(原来跑在腾讯云 1Mbps 的境内服务器上,SSL 握手时间都能让人抓狂)。

明确需求

  • 访问 Google 要保证安全且快,这里选择 VLESS 方案
  • 需要同时兼容多域名站点,这里有几个方案可选:单证书多域名 + VLESS 回落,前置 Nginx 分流,前置 HAProxy 分流

方案选则

VLESS 回落方案

由于 VLESS 本身不支持根据 SNI 来分流,需要支持多域名就需要签在同一张证书上。Let’s Encrypt 已经对泛域名证书有了比较完美的支持,配合 acme.sh 很容易实现单证书多域名(我所有的域名都在 Cloudflare 下解析)

然后这仍然有些一些问题,VLESS fallback 方案对 Typecho 的支持并不好(翻了一下其他博文,对 WordPress 之类的 php 程序支持也不行),所以无奈放弃这种方案(其实我也不太想将 VLESS 作为前置的 TCP 分流器)。于是开始尝试 Nginx 前置分流方案

Nginx 前置分流

借助 Nginx 的 ssl_preread 模块来实现 SNI,配置起来也很简单,只需要增加 stream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
stream {
map $ssl_preread_server_name $backend_name {
v2.example.com vless;
default web;
}
upstream web {
server 127.0.0.1:8443;
}
upstream vless {
server 127.0.0.1:10443;
}
server {
listen 443 reuseport;
proxy_pass $backend_name;
ssl_preread on;
}
}

然后在站点配置里增加对上述 web 端口的监听即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
listen 8443 ssl;
server_name web1.example.com;
root /home/www/web1;

ssl_certificate /home/ssl/cert.pem;
ssl_certificate_key /home/ssl/key.pem;
}
server {
listen 8443 ssl;
server_name web2.example.com;
root /home/www/web2;

ssl_certificate /home/ssl/cert.pem;
ssl_certificate_key /home/ssl/key.pem;
}

其实这种方案本来也没什么问题,我开始也觉得可以就这样了。之后无意间发现博客后台记录的一些 client_ip 是 127.0.0.1,于是乎又不得不折腾 HAProxy 方案来分流

HAProxy 前置分流

HAProxy 相较于 ngx_stream_ssl_preread_module 的优点就是可以为每一个不同的 backend 设置不一样的数据传递方式,正是因为 nginx 做不到这一点所以上一个方案才会被 pass 掉

正巧 Proxy Protocol 就是用来做这个事情的,正巧 HAProxy 和 Nginx 对这个的支持都不错。相比而言 HAProxy 的配置文件 haproxy.cfg 显得就比较简单一点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
timeout connect 24h
timeout client 24h
timeout server 24h

frontend ssl
mode tcp
bind *:443

tcp-request inspect-delay 3s
tcp-request content accept if { req.ssl_hello_type 1 }

use_backend vless if { req_ssl_sni -i v2.example.com }
default_backend nginx

backend nginx
mode tcp
server nginx 127.0.0.1:8443 send-proxy

backend vless
mode tcp
server vless 127.0.0.1:10443

同样的对应 Nginx 站点配置也需要加上对 Proxy Protocol 的支持:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
server {
listen 8443 ssl proxy_protocol;
server_name web1.example.com;
root /home/www/web1;

set_real_ip_from 127.0.0.1;
real_ip_header proxy_protocol;

ssl_certificate /home/ssl/cert.pem;
ssl_certificate_key /home/ssl/key.pem;
}
server {
listen 8443 ssl proxy_protocol;
server_name web2.example.com;
root /home/www/web2;

set_real_ip_from 127.0.0.1;
real_ip_header proxy_protocol;

ssl_certificate /home/ssl/cert.pem;
ssl_certificate_key /home/ssl/key.pem;
}

一些提示

  1. 部分软件的安装方式和文档基本都可以在 https://github.com/v2fly 找到
  2. 需要做 http 跳 https 的话直接 Nginx 监听 80 跳 https://$server_name$request_uri 即可
  3. 因为 Nginx 的跳转是默认使用的是绝对地址,上述配置存在 https://web1.example.com/pathhttps://web1.example.com:8443/path/ 的情况,需要使用 absolute_redirect off 设置为相对地址的跳转

更新

2021.04.10 更新

翻阅 VLESS 文档无意间发现其入站支持 Proxy Protocol,果断卸载 HAProxy,用回上文中所说的 Nginx 分流方案,只需要在 stream.server 的配置块里新加一行 proxy_protocol on,同时 VLESS 的配置项 streamSettings.tcpSettings 中加入 "acceptProxyProtocol": true 即可

2022.06.04 更新

翻阅 xray 文档 发现已经支持了 SNI 分流功能,所以如果没有什么很复杂的负载均衡需求,完全靠 xray 也足够了

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
总字数 24.2k 访客数 访问量