【nginx】用nginx stream 模块实现基于SNI的四层(TCP)代理服务

局域网中只有1台服务器可以访问外部网络,其他服务器如果要访问第三放地址只能通过代理器,其他服务器在/etc/hosts下手动添加解析条目,将第三放地址的域名都解析到这台代理服务器的ip。


> # cat  /usr/local/nginx/conf/nginx.conf

```
user nginx nginx;
worker_processes auto;

error_log /usr/local/nginx/logs/error.log;
pid       /usr/local/nginx/nginx.pid;

events {
    use epoll;
    worker_connections 65535;
}

stream {

# HTTP
    map $ssl_preread_server_name $backend_http {
        www.80.com              www_80_com;
        www.8080.com            www_8080_com;
        }
        
        
# HTTPS    
    map $ssl_preread_server_name $backend_https {
        www.443.com             www_443_com;
        www.1443.com            www_1443_com;
    }
        

# 定义需要代理的各地址的 upstream
      
    upstream www_80_com {
        server www.80.com:80;
    }

    upstream www_8080_com {
        server www.8080.com:8080;
    }    
    
    upstream www_443_com {
        server www.443.com:443;
    }

    upstream www_1443_com {
        server www.1443.com:1443;

    server {
               listen 80;
               resolver 192.168.1.110;
               proxy_pass $backend_http;      
               proxy_connect_timeout 300s;
               proxy_timeout 300s;
               proxy_next_upstream_timeout 300s;
    }


    server {
               listen 8080;
               resolver 192.168.1.110;
               proxy_pass $backend_http;      
               proxy_connect_timeout 300s;
               proxy_timeout 300s;
               proxy_next_upstream_timeout 300s;
    }

    server {
               listen 443;
               ssl_preread on;
               resolver 192.168.1.110;
               proxy_pass $backend_https;
               proxy_connect_timeout 300s;
               proxy_timeout 300s;
               proxy_next_upstream_timeout 300s;
    }
        
    server {
                listen 1443;
                ssl_preread on;
                resolver 192.168.1.110;
                proxy_pass $backend_https;
                proxy_connect_timeout 300s;
                proxy_timeout 300s;
                proxy_next_upstream_timeout 300s;
        }
}


```

> nginx stream 代理配置解读

这个配置文件是Nginx的`stream`模块配置,主要用于四层(TCP/UDP)代理和负载均衡功能,而不是通常的七层(HTTP)代理。

## 配置结构分析

### 1. 基础配置部分
```nginx
user nginx nginx;
worker_processes auto;

error_log /usr/local/nginx/logs/error.log;
pid/usr/local/nginx/nginx.pid;

events {
use epoll;
worker_connections 65535;
}
```
- 以`nginx`用户和组运行
- 自动设置worker进程数量
- 错误日志和pid文件路径
- 事件模块使用epoll高效模型,每个worker支持65535个连接

### 2. Stream模块核心功能

#### 2.1 域名映射(map指令)
```nginx
# HTTP
map $ssl_preread_server_name $backend_http {
www.80.com      www_80_com;
www.8080.com  www_8080_com;
}

# HTTPS
map $ssl_preread_server_name $backend_https {
www.443.com    www_443_com;
www.1443.com  www_1443_com;
}
```
- 创建了两个映射表,根据SNI(Server Name Indication)信息将域名映射到对应的upstream
- `$ssl_preread_server_name`是Nginx在SSL握手前读取的客户端请求的服务器名称
- 第一个映射处理HTTP流量(非加密),第二个处理HTTPS流量(加密)

#### 2.2 Upstream定义
```nginx
upstream www_80_com {
server www.80.com:80;
}

upstream www_8080_com {
server www.8080.com:8080;
}

upstream www_443_com {
server www.443.com:443;
}

upstream www_1443_com {
server www.1443.com:1443;
}
```
- 定义了4个后端服务器组
- 每个upstream对应一个特定的域名和端口组合

#### 2.3 服务器监听配置

**HTTP代理服务器(端口80):**
```nginx
server {
listen 80;
resolver 192.168.1.110;
proxy_pass $backend_http;
proxy_connect_timeout 300s;
proxy_timeout 300s;
proxy_next_upstream_timeout 300s;
}
```
- 监听80端口(HTTP)
- 使用DNS解析器192.168.1.110
- 根据`$backend_http`映射表转发流量
- 各种超时设置为300秒

**HTTP代理服务器(端口8080):**
```nginx
server {
listen 8080;
resolver 192.168.1.110;
proxy_pass $backend_http;
proxy_connect_timeout 300s;
proxy_timeout 300s;
proxy_next_upstream_timeout 300s;
}
```
- 类似80端口配置,但监听8080端口

**HTTPS代理服务器(端口443):**
```nginx
server {
listen 443;
ssl_preread on;
resolver 192.168.1.110;
proxy_pass $backend_https;
proxy_connect_timeout 300s;
proxy_timeout 300s;
proxy_next_upstream_timeout 300s;
}
```
- 监听443端口(HTTPS)
- 启用`ssl_preread`以获取SNI信息
- 根据`$backend_https`映射表转发流量

**HTTPS代理服务器(端口1443):**
```nginx
server {
listen 1443;
ssl_preread on;
resolver 192.168.1.110;
proxy_pass $backend_https;
proxy_connect_timeout 300s;
proxy_timeout 300s;
proxy_next_upstream_timeout 300s;
}
```
- 类似443端口配置,但监听1443端口

## 功能总结

这个配置实现了一个**基于SNI的四层(TCP)代理服务器**,主要功能包括:

1. **透明代理**:根据客户端请求的域名将流量转发到不同的后端服务器
2. **多端口支持**:同时支持HTTP(80,8080)和HTTPS(443,1443)流量的代理
3. **域名路由**:使用SNI信息在SSL握手前就确定后端服务器
4. **TCP负载均衡**:虽然每个upstream只有一个服务器,但可以扩展为多服务器实现负载均衡

这种配置常用于:
- 需要基于域名的TCP层路由
- SSL/TLS终止前的流量转发
- 多域名共享同一IP和端口的场景
- 需要透明代理而不修改数据包内容的场景

注意:这不是传统的HTTP反向代理,而是在更底层的TCP/UDP层工作的代理。

> ssl_preread  配置的作用是什么 ?  

`ssl_preread` 是 Nginx 在 **Stream 模块(四层代理)** 中用于 **SNI(Server Name Indication)解析** 的关键指令,它使得 Nginx 可以在不解密 TLS/SSL 流量的情况下,提前读取客户端请求的域名信息,从而进行路由决策。

---

## **`ssl_preread` 的作用**
1. **读取 SNI 信息**
- 在 TLS/SSL 握手阶段,客户端(如浏览器)会发送 `SNI` 扩展字段,告诉服务器它要访问的域名(如 `www.www.example.com`)。
- `ssl_preread` 让 Nginx 在不解密 TLS 数据的情况下,提前读取这个 SNI 信息。

2. **基于域名的 TCP 层路由**
- 结合 `map` 指令,Nginx 可以根据 `$ssl_preread_server_name` 变量将流量代理到不同的后端服务器(如 `upstream`)。
- 例如:
```nginx
map $ssl_preread_server_name $backend {
www.example.com    backend1;
www.test.com    backend2;
}
```
这样,访问 `www.example.com:443` 的流量会被代理到 `backend1`,而 `www.test.com:443` 的流量会被代理到 `backend2`。

3. **支持 HTTPS 透明代理**
- 传统的 HTTP 反向代理(`http` 模块)可以解析 `Host` 头来路由请求,但 HTTPS 流量是加密的,Nginx 无法直接读取 `Host` 头。
- `ssl_preread` 允许 Nginx 在不解密 TLS 的情况下,仅读取 SNI 信息,实现类似 HTTP 代理的路由功能。

4. **提高性能(不解密 TLS)**
- 由于 Nginx 只是读取 SNI,而不进行 TLS 解密,它的性能比完整的 TLS 终止(解密后再代理)更高,适用于透明代理场景。

---

## **`ssl_preread` 的典型应用场景**
1. **多域名 HTTPS 代理**
- 多个 HTTPS 域名(如 `a.com:443` 和 `b.com:443`)共享同一个 IP 和端口,Nginx 根据 SNI 将流量转发到不同的后端服务器。

2. **CDN 或 LB(负载均衡)**
- 在四层负载均衡中,基于 SNI 路由不同的 TLS 流量到对应的后端服务。

3. **透明代理(如上网工具)**
- 某些代理工具利用 SNI 信息进行流量分类和转发。

4. **端口复用**
- 例如,让 `:443` 同时代理 HTTP/3 (QUIC)、HTTPS、Trojan 等不同协议,通过 SNI 区分。

---

## **示例配置**
```nginx
stream {
# 定义 SNI 到 upstream 的映射
map $ssl_preread_server_name $backend {
www.example.com  backend_https01;
www.test.com     backend_https02;
}

# 定义后端服务器
upstream backend_https01 {
server 1.2.3.4:443;
}

upstream backend_https02 {
server 5.6.7.8:443;
}

# 监听 443,启用 ssl_preread
server {
listen 443;
ssl_preread on;
proxy_pass $backend;
}
}
```
- 当客户端访问 `www.example.com:443` 时,Nginx 会读取 SNI,发现是 `www.example.com`,然后代理到 `backend_https01`(1.2.3.4:443)。
- 访问 `www.test.com:443` 则代理到 `backend_https02`(5.6.7.8:443)。

---

## **`ssl_preread` vs `ssl`(TLS 终止)**
| 特性| `ssl_preread`(SNI 路由) | `ssl`(TLS 终止) |
|--------------|--------------------------|------------------|
| **是否解密 TLS** | ❌ 不解密,仅读取 SNI | ✅ 解密 TLS |
| **适用场景** | 透明代理、四层 LB | HTTPS 反向代理 |
| **性能** | 更高(不解密) | 较低(需解密) |
| **能否修改内容** | ❌ 不能(TCP 层) | ✅ 能(HTTP 层) |

---

## **总结**
- `ssl_preread` 主要用于 **在不解密 TLS 的情况下读取 SNI**,实现基于域名的四层代理。
- 适用于 **HTTPS 透明代理、多域名路由、CDN、科学上网工具** 等场景。
- 相比完整的 TLS 终止(`ssl`),它的性能更高,但不能修改加密内容。

如果你的需求是 **"不解密 TLS,但要根据域名路由"**,`ssl_preread` 是最佳选择!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值