ssl_preread是基于L4的反代方案,TLS SNI握手时客户端会提供域名,所以可以让Nginx在无需完成TLS握手的情况下,就根据域名进行后端服务器的选择。简单来讲,你只需要给后端服务器配置一个SSL证书,而提供反代功能的Nginx则无需配置。文档见此:http://nginx.org/en/docs/stream/ngx_stream_ssl_preread_module.html
因为这是L4反代,所以通过“proxy_set_header”传递客户端IP的方法是行不通的。其实传递客户端IP的解决办法跟上一篇文章类似:L4(传输层)IP透明反向代理的实现(传递客户端真实IP),nginx也是实现了IP_TRANSPARENT的:https://www.nginx.com/blog/ip-transparency-direct-server-return-nginx-plus-transparent-proxy#ip-transparency,所以,在server里面加上下面一行:
1 |
proxy_bind $remote_addr transparent; |
然后按前一篇文章的思路,对路由表进行配置即可。
提醒一下,ssl_preread是配置在stream block下的,放在别的地方,会提示:
1 |
nginx: [emerg] "ssl_preread" directive is not allowed here in /etc/nginx/nginx.conf |
所以正确的nginx.conf结构应该如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
... http { ... server { ... } server { ... } ... } ... stream { map $ssl_preread_server_name $name { backend.example.com backend; default backend2; } upstream backend { server 192.168.0.1:12345; server 192.168.0.2:12345; } upstream backend2 { server 192.168.0.3:12345; server 192.168.0.4:12345; } server { listen 12346; proxy_pass $name; proxy_bind $remote_addr transparent; ssl_preread on; } } ... |
另外,如果在stream中listen了443,http中就无法listen 443了,启动nginx会提示:
1 |
nginx: [emerg] bind() to 0.0.0.0:443 failed (98: Address already in use) |
可以让http中的server listen loopback地址的另一个端口,让stream中的server proxy_pass这个loopback地址的端口,例如127.0.2.1:8443,然后通过127.0.2.1做策略路由:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
... http { ... server { ... listen 127.0.2.1:8443 ssl http2; port_in_redirect off; # 重要:阻止nginx重定向到此Server listen的端口 ... } server { ... } ... } ... stream { map $ssl_preread_server_name $name { localbackend.com local_backend; # 这里指定使用http中的server backend.example.com backend; default backend2; } upstream backend { server 192.168.0.1:12345; server 192.168.0.2:12345; } upstream backend2 { server 192.168.0.3:12345; server 192.168.0.4:12345; } # 对应http中的server upstream local_backend { server 127.0.2.1:443; } server { listen 443; proxy_pass $name; proxy_bind $remote_addr transparent; # 加了这个才能传递客户端IP ssl_preread on; } } ... |
然后配置策略路由:
1 2 |
root@nginx:~# ip rule add from 127.0.2.1 lookup 61 root@nginx:~# ip route add local 0.0.0.0/0 dev lo table 61 |