TECH_DOC.md 23 KB

技术方案文档 (TECH_DOC)

Nginx Stream + SSL Preread 自动分流方案 — 完整技术文档

版本:v2.0
最后更新:2026-10-06
维护者:steven_roc
持续更新:本文档随项目演进持续更新


目录

  1. 方案概述
  2. 架构设计
  3. 配置文件结构
  4. 环境要求
  5. 软件依赖
  6. 端口规划
  7. 配置详解
  8. SSL 证书管理
  9. 安全策略
  10. 日志管理
  11. 性能调优
  12. 部署与运维
  13. 故障排查
  14. 端口扩展指南

1. 方案概述

1.1 核心目标

通过 Nginx 的 stream 模块 + ssl_preread 功能,在 TCP 层(OSI 第 4 层)检测入站流量的协议类型,实现:

  • TLS 流量 → 由 Nginx HTTP 模块进行 SSL 终结 → 反向代理到内网 HTTP 服务
  • 明文 HTTP 流量 → 301 永久重定向到对应端口的 HTTPS 地址

用户无论输入 http:// 还是 https://,都能自动获得 HTTPS 加密访问。

1.2 多子域名架构

本方案支持多个子域名共享同一服务器 IP,通过不同端口区分服务:

域名 外网端口 后端服务 用途
chanking.zhonjin.com 40130 127.0.0.1:5004 主 Web 服务
mqtt.zhonjin.com 40715 127.0.0.1:8085 MQTT 服务
baolin.zhonjin.com 40716 127.0.0.1:5000 宝林服务 A
baolin.zhonjin.com 40719 127.0.0.1:5001 宝林服务 B

1.3 设计原则

原则 说明
TCP 层分流 stream 模块工作在 TCP 层,不解密 TLS,仅通过 ClientHello 预读判断协议
SSL 终结集中化 所有 SSL 证书由 Nginx 统一管理,后端服务只需提供 HTTP
端口隔离 每个外网端口独立 stream server + 独立 map 变量,互不干扰
域名隔离 不同子域名通过独立的 server_name 配置区分,支持各自的后端服务
配置模块化 主配置与子域名配置分离,便于独立维护
向后兼容 新增服务组不影响已有配置,所有模块松耦合

2. 架构设计

2.1 整体架构图

                       外网用户请求
                            │
              ┌─────────────┼─────────────┐
              │             │             │
        chanking:40130  mqtt:40715  baolin:40716/40719
              │             │             │
              └─────────────┼─────────────┘
                            │
                  ┌─────────┴─────────┐
                  │  Stream 模块       │
                  │  ssl_preread on   │
                  │  协议检测(TCP层)    │
                  └─────────┬─────────┘
                            │
               ┌────────────┼────────────┐
               │ TLS        │            │ 明文 HTTP
               ▼            │            ▼
        ┌────────────┐     │     ┌────────────┐
        │ HTTPS 服务  │     │     │ HTTP 服务   │
        │ (SSL 终结)  │     │     │ (301 跳转)  │
        │ :5005      │     │     │ :5006      │
        │ :5007      │     │     │ :5008      │
        │ :5009      │     │     │ :5010      │
        │ :5011      │     │     │ :5012      │
        └──────┬─────┘     │     └──────┬─────┘
               │           │            │
               └───────────┼────────────┘
                           │
                 ┌─────────┴─────────┐
                 │  反向代理          │
                 │  proxy_pass       │
                 └─────────┬─────────┘
                           │
              ┌────────────┼────────────┐
              ▼            ▼            ▼
        ┌──────────┐ ┌──────────┐ ┌──────────┐
        │ :5004    │ │ :8085    │ │ :5000    │  ...
        │ 内网服务  │ │ 内网服务  │ │ 内网服务  │
        └──────────┘ └──────────┘ └──────────┘

2.2 数据流详解

HTTPS 请求流程:

  1. 用户访问 https://mqtt.zhonjin.com:40715
  2. TCP 连接到达 Nginx stream 模块监听的 40715 端口
  3. ssl_preread on 预读 ClientHello,$ssl_preread_protocol 值为 TLS
  4. map $backend_target_40715 匹配 default → 转发到 127.0.0.1:5007
  5. HTTP 模块 5007 端口接收 TLS 连接,完成 SSL 终结
  6. proxy_pass 将请求转发到后端 http://127.0.0.1:8085
  7. 后端响应通过 Nginx 加密后返回用户

HTTP 请求流程:

  1. 用户访问 http://baolin.zhonjin.com:40716
  2. TCP 连接到达 Nginx stream 模块
  3. ssl_preread 预读失败(非 TLS),$ssl_preread_protocol 值为空 ""
  4. map $backend_target_40716 匹配 "" → 转发到 127.0.0.1:5010
  5. HTTP 模块 5010 端口返回 301 Moved Permanently,Location: https://baolin.zhonjin.com:40716$request_uri
  6. 浏览器自动重定向到 HTTPS 地址

3. 配置文件结构

3.1 文件布局

/etc/nginx/
├── nginx.conf                          # 主配置(全局参数 + 40130 端口 + include 指令)
├── conf.d/
│   └── zhonjin_http.conf               # 子域名 HTTP 服务配置(mqtt/baolin)
└── stream.d/
    └── zhonjin_stream.conf             # 子域名 Stream 分流配置(mqtt/baolin)

3.2 文件职责

文件 职责 include 位置
nginx.conf 全局参数、服务组1(40130)、include 指令 主配置入口
zhonjin_http.conf 子域名的 HTTPS server + HTTP 跳转 server http 块内
zhonjin_stream.conf 子域名的 stream map + stream server stream 块内

3.3 模块化优势

  • 独立维护:新增子域名只需编辑对应的 include 文件,不影响主配置
  • 减少冲突:不同团队的配置不会互相干扰
  • 易于审计:配置分散在小文件中,便于审查
  • 热重载安全:nginx -t 可验证所有 include 文件

4. 环境要求

4.1 操作系统

操作系统 版本要求 备注
Ubuntu 22.04+ 推荐,原生支持 Nginx 1.24+
Ubuntu 24.04 完全兼容,推荐用于新部署
Debian 12+ 原生支持
CentOS/RHEL 9+ 需使用 EPEL 源
Raspberry Pi OS Bookworm+ 兼容 Ubuntu 24.04 方案

4.2 Nginx 版本要求

功能 最低版本 推荐版本 说明
stream 模块 1.9.0+ 1.26+ TCP 层代理核心
ssl_preread 1.11.5+ 1.26+ TCP 层 TLS 预读
ssl_preread_buffer_size 1.23.4+ 1.26+ 可选,大 ClientHello 支持
http2 指令 1.25.1+ 1.26+ 新版 http2 on 指令
stream include 1.26+ 1.26+ stream 块内 include 指令

4.3 硬件要求

资源 最低配置 推荐配置 说明
CPU 1 核 2+ 核 stream 模块 CPU 消耗较低
内存 256MB 512MB+ SSL session cache 占用内存
磁盘 1GB 可用 5GB+ 日志存储需要空间
网络 100Mbps 1Gbps 取决于业务流量

5. 软件依赖

5.1 必装软件

# Ubuntu/Debian
sudo apt update
sudo apt install -y nginx-full    # nginx-full 包含 stream 模块
sudo apt install -y certbot python3-certbot-nginx   # Let's Encrypt 证书管理

# 验证 stream 模块已加载
nginx -V 2>&1 | grep -o with-stream
# 预期输出: with-stream

5.2 模块确认

# 动态模块方式(如使用动态加载)
ls /usr/lib/nginx/modules/ngx_stream_module.so

# 静态编译方式(nginx -V 检查)
nginx -V 2>&1 | grep -E "with-stream|with-http_ssl_module|with-http_v2_module"

5.3 可选工具

工具 用途 安装命令
htop 进程监控 apt install htop
tcpdump 网络抓包调试 apt install tcpdump
openssl 证书/SSL 调试 apt install openssl
curl HTTP 测试 apt install curl
logrotate 日志轮转(系统自带) 默认已安装

6. 端口规划

6.1 完整端口映射表

域名 外网端口 HTTPS 内网端口 HTTP 跳转端口 后端服务 用途
chanking.zhonjin.com 40130 5005 5006 127.0.0.1:5004 主 Web 服务
mqtt.zhonjin.com 40715 5007 5008 127.0.0.1:8085 MQTT 服务
baolin.zhonjin.com 40716 5009 5010 127.0.0.1:5000 宝林服务 A
baolin.zhonjin.com 40719 5011 5012 127.0.0.1:5001 宝林服务 B

6.2 端口命名规则

  • 外网端口(4xxxx):stream 模块监听,对外暴露
  • HTTPS 内网端口(50xx):HTTP 模块 SSL 终结入口
  • HTTP 跳转端口(50xx):HTTP 模块 301 跳转入口
  • 后端服务端口:实际业务服务监听端口

6.3 防火墙配置

# UFW 防火墙
sudo ufw allow 40130/tcp
sudo ufw allow 40715/tcp
sudo ufw allow 40716/tcp
sudo ufw allow 40719/tcp

# 或使用 iptables
sudo iptables -A INPUT -p tcp --dport 40130 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 40715 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 40716 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 40719 -j ACCEPT

注意:内部端口(5005-5012)无需对外开放,stream 模块通过 localhost 转发即可。


7. 配置详解

7.1 主配置(nginx.conf)

主配置文件包含:

  • 全局参数(worker、events、日志格式)
  • load_module 加载 stream 模块
  • 服务组 1(chanking.zhonjin.com:40130)的完整配置
  • include 指令引入子域名配置

    # nginx.conf 末尾 include 指令
    http {
    # ... 服务组 1 配置 ...
    include /etc/nginx/conf.d/zhonjin_http.conf;
    }
    
    stream {
    # ... 服务组 1 的 stream 配置 ...
    include /etc/nginx/stream.d/zhonjin_stream.conf;
    }
    

7.2 Stream 模块配置

stream {
    # 独立 map 变量(每个端口唯一)
    map $ssl_preread_protocol $backend_target_40715 {
        ""      127.0.0.1:5008;     # 明文HTTP → 301跳转
        default 127.0.0.1:5007;     # TLS流量 → SSL入口
    }

    server {
        listen 40715 backlog=4096;
        tcp_nodelay on;
        ssl_preread on;
        proxy_pass $backend_target_40715;
        proxy_connect_timeout 10s;
        proxy_timeout        3600s;
        proxy_next_upstream   off;
        access_log /var/log/nginx/stream_access_40715.log stream_main;
    }
}

关键参数说明:

参数 值 说明
backlog 4096 TCP 监听队列长度,高并发时增大
tcp_nodelay on 禁用 Nagle 算法,减少小包延迟
ssl_preread on 只读预读 ClientHello,不解密
proxy_connect_timeout 10s 与后端建立连接的超时
proxy_timeout 3600s 连接空闲超时,WebSocket 长连接需设大
proxy_next_upstream off 禁用重试,防止非幂等请求重复执行

7.3 HTTPS Server 配置

server {
    listen 127.0.0.1:5007 ssl;
    http2 on;
    server_name mqtt.zhonjin.com;

    ssl_certificate     /etc/letsencrypt/live/zhonjin.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/zhonjin.com/privkey.pem;
    ssl_session_cache   shared:SSL_mqtt:5m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         ECDHE-ECDSA-AES128-GCM-SHA256:...;
    ssl_prefer_server_ciphers off;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    location / {
        proxy_pass http://127.0.0.1:8085;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        $connection_upgrade;
        proxy_connect_timeout 5s;
        proxy_read_timeout    3600s;
        proxy_send_timeout    60s;
    }
}

7.4 HTTP 跳转配置

server {
    listen 127.0.0.1:5008;
    server_name mqtt.zhonjin.com;
    return 301 https://$host:40715$request_uri;
}

注意:301 跳转 URL 中的端口号必须与对应的外网端口一致。$host 变量会自动保留用户访问的域名。


8. SSL 证书管理

8.1 通配符证书方案

所有子域名共用同一个通配符证书:

证书覆盖域名:
  - zhonjin.com
  - *.zhonjin.com(覆盖所有子域名)
    ├── chanking.zhonjin.com
    ├── mqtt.zhonjin.com
    └── baolin.zhonjin.com

8.2 证书申请

# 申请通配符证书(DNS 验证)
sudo certbot certonly --manual --preferred-challenges dns \
    -d zhonjin.com -d "*.zhonjin.com"

按提示添加 DNS TXT 记录完成验证。

8.3 证书路径

文件 路径 说明
证书链 /etc/letsencrypt/live/zhonjin.com/fullchain.pem 包含中间证书
私钥 /etc/letsencrypt/live/zhonjin.com/privkey.pem 证书私钥
根证书 /etc/letsencrypt/live/zhonjin.com/chain.pem 仅中间 CA

8.4 自动续期

# 测试续期
sudo certbot renew --dry-run

# 添加 crontab 自动续期
sudo crontab -e
# 添加以下行(每天凌晨 3 点检查)
0 3 * * * certbot renew --quiet --post-hook "nginx -s reload"

8.5 证书监控

# 检查证书有效期
openssl x509 -in /etc/letsencrypt/live/zhonjin.com/fullchain.pem -noout -dates

# 检查证书域名覆盖
openssl x509 -in /etc/letsencrypt/live/zhonjin.com/fullchain.pem -noout -text | grep -A1 "Subject Alternative Name"

9. 安全策略

9.1 SSL/TLS 安全配置

配置项 值 说明
ssl_protocols TLSv1.2 TLSv1.3 禁用 SSLv3/TLSv1.0/1.1
ssl_session_tickets off 禁用 session tickets,增强前向安全
ssl_prefer_server_ciphers off 现代推荐做法,优先客户端偏好
HSTS max-age=31536000 强制浏览器使用 HTTPS,1 年有效期

9.2 网络安全

  • 内部端口不暴露:所有 HTTP/HTTPS server 仅监听 127.0.0.1,外部无法直接访问
  • server_tokens off:隐藏 Nginx 版本号
  • 防火墙仅开放外网端口:40130, 40715, 40716, 40719
  • 证书统一管理:通配符证书覆盖所有子域名

9.3 建议加固项

# 可选:限制请求体大小(在 http 块中)
client_max_body_size 100m;

# 可选:添加安全响应头
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;

10. 日志管理

10.1 日志文件清单

日志文件 类型 说明
/var/log/nginx/error.log error 全局错误日志
/var/log/nginx/access.log access HTTP 服务访问日志
/var/log/nginx/stream_error.log error Stream 模块错误日志
/var/log/nginx/stream_access_40130.log access Stream 端口 40130 访问日志
/var/log/nginx/stream_access_40715.log access Stream 端口 40715 访问日志
/var/log/nginx/stream_access_40716.log access Stream 端口 40716 访问日志
/var/log/nginx/stream_access_40719.log access Stream 端口 40719 访问日志

10.2 Logrotate 配置

# /etc/logrotate.d/nginx(系统通常已自带)
/var/log/nginx/*.log {
    daily
    missingok
    rotate 52
    compress
    delaycompress
    notifempty
    create 0640 www-data adm
    sharedscripts
    postrotate
        [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
    endscript
}

10.3 日志分析

# 查看最近 100 条 stream 访问记录
tail -100 /var/log/nginx/stream_access_40715.log

# 统计各端口流量
wc -l /var/log/nginx/stream_access_*.log

# 查看错误日志
tail -f /var/log/nginx/stream_error.log

11. 性能调优

11.1 内核参数优化

# /etc/sysctl.conf 添加
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_keepalive_time = 600
sudo sysctl -p

11.2 Nginx 工作进程

worker_processes auto;
worker_rlimit_nofile 65535;

events {
    worker_connections 8192;
    multi_accept on;
    use epoll;
}

11.3 SSL 性能优化

ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 114.114.114.114 valid=300s;
resolver_timeout 5s;

12. 部署与运维

12.1 部署步骤

# 1. 安装 Nginx(含 stream 模块)
sudo apt install -y nginx-full certbot python3-certbot-nginx

# 2. 申请通配符 SSL 证书
sudo certbot certonly --manual --preferred-challenges dns \
    -d zhonjin.com -d "*.zhonjin.com"

# 3. 创建配置目录
sudo mkdir -p /etc/nginx/conf.d /etc/nginx/stream.d

# 4. 部署配置文件
sudo cp nginx.conf /etc/nginx/nginx.conf
sudo cp zhonjin_http.conf /etc/nginx/conf.d/zhonjin_http.conf
sudo cp zhonjin_stream.conf /etc/nginx/stream.d/zhonjin_stream.conf

# 5. 测试配置
sudo nginx -t

# 6. 重载 Nginx
sudo systemctl reload nginx

# 7. 配置防火墙
sudo ufw allow 40130/tcp
sudo ufw allow 40715/tcp
sudo ufw allow 40716/tcp
sudo ufw allow 40719/tcp

# 8. 验证服务
curl -I http://chanking.zhonjin.com:40130     # 应返回 301
curl -I https://chanking.zhonjin.com:40130    # 应返回 200
curl -I http://mqtt.zhonjin.com:40715         # 应返回 301
curl -I https://mqtt.zhonjin.com:40715        # 应返回 200
curl -I http://baolin.zhonjin.com:40716       # 应返回 301
curl -I https://baolin.zhonjin.com:40716      # 应返回 200
curl -I http://baolin.zhonjin.com:40719       # 应返回 301
curl -I https://baolin.zhonjin.com:40719      # 应返回 200

12.2 日常运维命令

# 查看 Nginx 状态
sudo systemctl status nginx

# 重载配置(不中断服务)
sudo nginx -t && sudo systemctl reload nginx

# 查看当前监听端口
sudo ss -tlnp | grep nginx

# 查看连接数
sudo ss -tnp | grep nginx | wc -l

12.3 配置文件备份

# 备份当前配置
sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak.$(date +%Y%m%d)
sudo cp /etc/nginx/conf.d/zhonjin_http.conf /etc/nginx/conf.d/zhonjin_http.conf.bak.$(date +%Y%m%d)
sudo cp /etc/nginx/stream.d/zhonjin_stream.conf /etc/nginx/stream.d/zhonjin_stream.conf.bak.$(date +%Y%m%d)

13. 故障排查

13.1 常见问题及解决

问题 可能原因 排查方法 解决方案
nginx -t 报错 unknown directive stream 模块未加载 nginx -V 2>&1 \| grep stream 安装 nginx-full 或加载模块
nginx -t 报错 include 文件找不到 include 路径错误 检查文件是否存在 确认路径和目录权限
端口无响应 防火墙阻挡 sudo ufw status 放行端口
HTTP 不跳转 map 变量名冲突 检查 stream 中 map 变量是否唯一 每个端口使用独立变量名
SSL 握手失败 证书路径错误 openssl s_client -connect localhost:5007 检查证书路径和权限
域名解析错误 DNS 未配置 dig mqtt.zhonjin.com 添加 A 记录指向服务器 IP
WebSocket 断开 proxy_timeout 过短 检查 stream 和 http 超时配置 保持两者一致(3600s)
502 Bad Gateway 后端服务未启动 curl http://127.0.0.1:8085 启动后端服务
端口冲突 其他程序占用 sudo ss -tlnp \| grep :PORT 停止冲突程序或更换端口

13.2 调试技巧

# 1. TCP 层面验证 stream 转发
openssl s_client -connect mqtt.zhonjin.com:40715 -servername mqtt.zhonjin.com

# 2. 检查 SSL 证书链
echo | openssl s_client -connect mqtt.zhonjin.com:40715 -showcerts 2>/dev/null | openssl x509 -noout -dates

# 3. 查看 stream 模块流量
tail -f /var/log/nginx/stream_access_40715.log

# 4. 抓包分析 TCP 流量
sudo tcpdump -i any port 40715 -n -c 100

# 5. 验证 HTTP 跳转
curl -v http://mqtt.zhonjin.com:40715 2>&1 | grep Location

14. 端口扩展指南

14.1 新增端口映射步骤

当需要新增一组端口映射时(以 newservice.zhonjin.com:40800 → 后端 6000 为例):

Step 1:在 zhonjin_http.conf 中添加 server 配置

# HTTPS 服务
server {
    listen 127.0.0.1:NEW_HTTPS_PORT ssl;
    http2 on;
    server_name newservice.zhonjin.com;
    # ... SSL 配置(复制已有模式,注意 ssl_session_cache 名称唯一)
    location / {
        proxy_pass http://127.0.0.1:6000;
        # ... proxy 配置
    }
}

# HTTP 跳转
server {
    listen 127.0.0.1:NEW_HTTP_PORT;
    server_name newservice.zhonjin.com;
    return 301 https://$host:40800$request_uri;
}

Step 2:在 zhonjin_stream.conf 中添加分流配置

map $ssl_preread_protocol $backend_target_40800 {
    ""      127.0.0.1:NEW_HTTP_PORT;
    default 127.0.0.1:NEW_HTTPS_PORT;
}

server {
    listen 40800 backlog=4096;
    tcp_nodelay on;
    ssl_preread on;
    proxy_pass $backend_target_40800;
    proxy_connect_timeout 10s;
    proxy_timeout        3600s;
    proxy_next_upstream   off;
    access_log /var/log/nginx/stream_access_40800.log stream_main;
}

Step 3:更新防火墙并重载

sudo ufw allow 40800/tcp
sudo nginx -t && sudo systemctl reload nginx

14.2 端口编号建议

端口范围 用途 示例
40100-40199 核心 Web 服务 40130
40700-40799 扩展服务组 40715, 40716, 40719
40800-40899 预留扩展 -

附录

A. 配置文件清单

文件 路径 说明
nginx.conf 项目根目录 主配置
zhonjin_http.conf 项目根目录 HTTP 服务配置
zhonjin_stream.conf 项目根目录 Stream 分流配置
zhonjin.com.conf 项目根目录 配置总览说明

B. 相关文档