nginx stream realip使用

前言

由于要开始用nginx的tcp代理,发现最终http打印出来的IP都是最后一个代理的内网IP,这就不方便去查问题了。那就依样画葫芦吧。但是发现个问题,TCP层面哪里有header头啊。还好nginx提供了stream_realip这个模块。

1
./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

我实际的网络结构如下:

1
user1 ---> SLB --->  nginx tcp proxy1 ---> nginx tcp proxy2 ---> nginx http server

先说这个处理的思路吧。大家都知道tcp是没有header这些东西的,那就只能从data部分来着手了。TCP proxy_protocol的定义其实就是在数据报文最前面加上对应的IP信息。然后最后一个server解开这个data前面的IP信息。

但是一开始没有这个思路,就看了官方文档大干快上。
http://nginx.org/en/docs/stream/ngx_stream_realip_module.html

排查

把nginx tcp proxy1和proxy2的配置改成如下:

1
2
3
4
5
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的日志,发现没有生效啊。这文档骗人啊。

然后放狗搜了下,这个哥们一样的问题
https://stackoverflow.com/questions/40873393/nginx-real-client-ip-to-tcp-stream-backend
于是上面的配置又改成了如下:

1
2
3
4
5
6
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的日志

1
2
~ » curl -I http://api.timoq.com
curl: (52) Empty reply from server
1
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。
现象就跟下面这个的一样

https://stackoverflow.com/questions/46948866/verify-if-nginx-is-working-correctly-with-proxy-protocol-locally

这看着还是配置文件不对导致的。看来各种教程有问题啊。于是又仔细看了看了下官方文档
https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol/

这篇还是说的比较清楚的,也就是我前面总结的那个。那我们重新梳理一下

1
user1 ---> SLB(透明代理) --->  nginx tcp proxy1(在data头部增加realip) ---> nginx tcp proxy2(默认tcp传输,不改变数据包) ---> nginx http server(解开nginx tcp proxy1里在data里增加的realip)

去nginx http server抓包看了下,确实如此:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>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

1
2
3
4
5
6
server {
set_real_ip_from 172.16.0.0/16;
listen 21000 so_keepalive=on;
proxy_protocol on; #特别注意这个
proxy_pass goapi;
}

nginx tcp proxy2

1
2
3
4
5
server {
set_real_ip_from 172.16.0.0/16;
listen 21000 so_keepalive=on;
proxy_pass goapi;
}

nginx http server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
}
}