nginx stream分流,以及proxy_protocol
参考:https://code.evink.cn/2022/06/post/nginx-stream-module/
使用nginx的sni 对stream分流($ssl_preread_server_name)
为什么在stream中使用$ssl_preread_server_name,而在http中使用server_name
在 nginx 的 http 模块中,server_name 指令会首先判断请求是否是 SSL 请求。如果是 SSL 请求,nginx 将会读取 SNI(Server Name Indication)字段来获取请求的主机名。这是因为在 SSL/TLS 握手阶段,客户端会在 ClientHello 消息中包含 SNI 字段,用于指定请求的主机名。
如果不是 SSL 请求,nginx 就会读取 HTTP 请求头部中的 Host 字段来获取请求的主机名。这个 Host 字段包含了 HTTP 请求中的主机名信息,因此可以用来确定应该使用哪个 server 块来处理请求。
而在 stream 模块中,ssl_preread 指令专门用于解析 SNI 字段,因为 stream 模块处理的是 TCP 或 UDP 层面的流量,并不直接处理 HTTP 请求。因此,ssl_preread 指令只能在 TLS 握手阶段获取到 SNI 字段,而无法像 http 模块那样直接获取 HTTP 请求头部的 Host 字段
stream {
log_format stream_combined '$remote_addr [$time_local] $proxy_protocol_addr -> $upstream_addr [$protocol] $status $bytes_sent $bytes_received';
access_log /var/log/nginx/stream_access.log stream_combined;
#sni match
map $ssl_preread_server_name $backend_name {
tro.yourdomai tro;
# 域名都不匹配情况下的默认值
default web;
}
upstream tro {
server 127.0.0.1:7776;
}
#分流到web,这个可以是http模块的监听端口。
upstream web {
server 127.0.0.1:8443;
}
server {
listen 443 reuseport;
listen [::]:443 reuseport;
proxy_pass $backend_name;
ssl_preread on;
#proxy_protocol on; # 开启Proxy protocol,要求上游服务下支持proxy_protocal,不支持的话对于有些协议这个开启会产生影响导致处理出错。
}
}
关于proxy_protocal
proxy_protocol
是一种在 TCP 层上的协议,允许代理服务器向上游服务器传递客户端的真实 IP 地址和端口号等信息。
下面是再次解释 proxy_protocol
头部的示例:
phpCopy codePROXY <protocol> <client-ip> <proxy-ip> <client-port> <proxy-port>\r\n
PROXY
:指示这是PROXY
协议。<protocol>
:指示使用的协议类型,例如TCP4
或TCP6
。<client-ip>
:客户端的真实 IP 地址。<proxy-ip>
:代理服务器的 IP 地址。<client-port>
:客户端的端口号。<proxy-port>
:代理服务器的端口号。
所以,示例 PROXY TCP4 192.168.1.1 192.168.1.11 56324 443\r\n
的含义如下:
PROXY
:这是PROXY
协议。TCP4
:使用的是 IPv4 地址。192.168.1.1
:客户端的真实 IP 地址。192.168.1.11
:代理服务器的 IP 地址。56324
:客户端的端口号。443
:代理服务器的端口号。
这个示例表示客户端 IP 地址为 192.168.1.1
,端口号为 56324
,连接到代理服务器 192.168.1.11
的 443
端口。
当 Nginx 收到包含这样 PROXY
协议头部的连接时,它会根据这个头部信息将请求转发给上游服务器,并传递客户端的真实 IP 地址。