最近开始白嫖公司的香港云服务器了,与之前自购的美国 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,配置起来也很简单。

nginx.conf 里面增加 stream 块:

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;
    }
}

conf.d/web.conf 里增加对上述 web 端口的监听即可:

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

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 站点配置 conf.d/web.conf 也需要加上对 Proxy Protocol 的支持:

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 设置为相对地址的跳转
文章目录
文章目录