最近开始白嫖公司的香港云服务器了,与之前自购的美国 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

nginx.conf参考链接
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 端口的监听即可

web.conf
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 显得就比较简单一点:

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
  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 的支持:

web.conf
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 也足够了