IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Nginx ssl_preread传递客户端IP

    Zhensheng Yuan发表于 2021-02-12 09:33:01
    love 0

    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里面加上下面一行:

    proxy_bind $remote_addr transparent;

    然后按前一篇文章的思路,对路由表进行配置即可。

    提醒一下,ssl_preread是配置在stream block下的,放在别的地方,会提示:

    nginx: [emerg] "ssl_preread" directive is not allowed here in /etc/nginx/nginx.conf

    所以正确的nginx.conf结构应该如下:

    ...
    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会提示:

    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做策略路由:

    ...
    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;
        }
    }
    ...

    然后配置策略路由:

    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

     



沪ICP备19023445号-2号
友情链接