前言
由于要开始用nginx的tcp代理,发现最终http打印出来的IP都是最后一个代理的内网IP,这就不方便去查问题了。那就依样画葫芦吧。但是发现个问题,TCP层面哪里有header头啊。还好nginx提供了stream_realip这个模块。
/configure --with-http_geoip_module --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_v2_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module --with-file-aio --with-stream --with-stream_ssl_module --with-stream_realip_module --with-stream_geoip_module --with-stream_ssl_preread_module --with-pcre --prefix=/opt/nginx
我实际的网络结构如下:
user1 ---> SLB ---> nginx tcp proxy1 ---> nginx tcp proxy2 ---> nginx http server
先说这个处理的思路吧。大家都知道tcp是没有header这些东西的,那就只能从data部分来着手了。TCP proxy_protocol的定义其实就是在数据报文最前面加上对应的IP信息。然后最后一个server解开这个data前面的IP信息。
但是一开始没有这个思路,就看了官方文档大干快上。
Module ngx_stream_realip_module
排查
把nginx tcp proxy1和proxy2的配置改成如下:
server { set_real_ip_from 172.16.0.0/16; listen 21000 so_keepalive=on; proxy_pass goapi; }
其中172.16.0.0/16是当中传输的网段,然后看了下nginx http server的日志,发现没有生效啊。这文档骗人啊。
然后放狗搜了下,这个哥们一样的问题
Nginx real client IP to TCP stream backend - Stack Overflow
于是上面的配置又改成了如下:
server { set_real_ip_from 172.16.0.0/16; listen 21000 proxy_protocol so_keepalive=on; proxy_protocol on; proxy_pass goapi; }
结果这样改了,连服务都不通了,curl一下直接返回400了,看了下http server的日志
~ » curl -I http://api.timoq.com
curl: (52) Empty reply from server
172.16.106.16 api.timoq.com - [2019-12-25T19:43:50+08:00] "PROXY TCP4 172.16.0.6 172.16.0.16 21000 80" 400 631 "-" "-" "-" 0.000|-|-|-|-||-|-$
nginx http server收到的请求变成了”PROXY TCP4 172.16.0.6 172.16.0.16 40173 80”, 而不是实际的URL。
现象就跟下面这个的一样
Verify if nginx is working correctly with Proxy Protocol locally - Stack Overflow
这看着还是配置文件不对导致的。看来各种教程有问题啊。于是又仔细看了看了下官方文档
Accepting the PROXY Protocol | NGINX Documentation
这篇还是说的比较清楚的,也就是我前面总结的那个。那我们重新梳理一下
user1 ---> SLB(透明代理) ---> nginx tcp proxy1(在data头部增加realip) ---> nginx tcp proxy2(默认tcp传输,不改变数据包) ---> nginx http server(解开nginx tcp proxy1里在data里增加的realip)
去nginx http server抓包看了下,确实如此:
>E@@I0j ueWbQ/OH fhPROXY TCP4 222.73.97.11 192.168.0.6 53136 21000 HEAD /aaaaa HTTP/1.1 Host: api.timoq.com User-Agent: curl/7.54.0 Accept: */* >E@@I0j ueWbQ/OH fhPROXY TCP4 222.73.97.11 192.168.0.6 53136 21080 HEAD /aaaaa HTTP/1.1 Host: api.timoq.com User-Agent: curl/7.54.0 Accept: */*
终结
因此最终的配置就是如下了:
nginx tcp proxy1
server { set_real_ip_from 172.16.0.0/16; listen 21000 so_keepalive=on; proxy_protocol on; #特别注意这个 proxy_pass goapi; }
nginx tcp proxy2
server { set_real_ip_from 172.16.0.0/16; listen 21000 so_keepalive=on; proxy_pass goapi; }
nginx http server
http{ log_format proxy '$proxy_protocol_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent"'; #注意这里proxy_protocol_addr这个变量 server { listen 80 proxy_protocol; #负责解开data头部数据 set_real_ip_from 172.16.0.0/16; proxy_http_version 1.1; proxy_set_header Connection ""; access_log /opt/logs/nginx/proxy_api.timoq.com proxy; server_name api.timoq.com; real_ip_header proxy_protocol; proxy_set_header X-Real-IP $proxy_protocol_addr; proxy_set_header X-Forwarded-For $proxy_protocol_addr; include conf.d/include/api.conf; } }