Nginx Stream 模块根据 TLS SNI 与 HTTP Host 分流 实现端口复用

投稿日時は 2023-12-10  1625 表示


从官方源安装 nginx 最新版本

curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor | tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

gpg --dry-run --quiet --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg

echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/mainline/debian $(cat /etc/os-release | grep VERSION_CODENAME | cut -d'=' -f2) nginx" | tee /etc/apt/sources.list.d/nginx.list

apt update

apt install nginx -y

默认 Stream 模块只能根据 SNI 分流,如果要通过 HTTP Header 中的 Host 分流需要自定义脚本才能实现

安装 nginx-module-njs 模块,以运行自定义脚本

apt install nginx-module-njs -y

保存以下内容到 /etc/nginx/http_server_name.js

var server_name = '-';

function read_server_name(s) {
  s.on('upload', function (data, flags) {
    if (data.length || flags.last) {
      s.done();
    }

    var n = data.indexOf('\r\nHost: ');
    if (n != -1) {

      var start_host = n + 8;
      var next_header = data.indexOf('\r\n', start_host);

      server_name = data.substr(start_host, next_header - start_host);

      var port_start = server_name.indexOf(':');
      if (port_start != -1) {
        server_name = server_name.substr(0, port_start);
      }
    }
  });
}

function get_server_name(s) {
  return server_name;
}

export default {read_server_name, get_server_name}

修改 /etc/nginx/nginx.conf 配置文件

示例

user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
# 加载 nginx-module-njs 模块
load_module modules/ngx_stream_js_module.so;

events {
    worker_connections 1024;
}

stream {
    # 加载自定义脚本
    js_import main from http_server_name.js;
    js_set $preread_server_name main.get_server_name;

    # 根据 TLS SNI 分流
    map $ssl_preread_server_name $upstream_443 {
        example1.com 127.0.0.1:12345;
        example2.com 127.0.0.1:23456;
        default 127.0.0.1:8443;
    }

    # 根据 HTTP Host 分流
    map $preread_server_name $upstream_80 {
        example1.com 127.0.0.1:34567;
        example2.com 127.0.0.1:45678;
        default 127.0.0.1:8080;
    }

    # 需要复用的 HTTPS 端口
    server {
        listen [::]:443 ipv6only=off;
        ssl_preread on;
        proxy_pass $upstream_443;
    }

    # 需要复用的 HTTP 端口
    server {
        listen [::]:80 ipv6only=off;
        js_preread main.read_server_name;
        proxy_pass $upstream_80;
    }
}

# 正常建站配置文件在 /etc/nginx/conf.d 目录下
# HTTP 模块监听端口不能与 Stream 模块重复
http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    gzip on;
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    include /etc/nginx/conf.d/*.conf;
}

重启 nginx

systemctl restart nginx.service

如遇启动失败可使用 nginx -t 命令检查配置 排查错误

最終更新日 2024-01-05