写点什么

在 nginx 中使用 proxy protocol 协议

作者:程序那些事
  • 2022 年 5 月 12 日
  • 本文字数:2759 字

    阅读完需:约 9 分钟

在nginx中使用proxy protocol协议

简介

我们已经介绍了 haproxy 提出的 proxy protocol 协议,通过 proxy protocol 协议,服务器端可以获得客户端的真实 IP 地址和端口,从而可以进行一些非常有意义的操作。


为什么获得客户端的真实 IP 地址会非常有意义呢?


考虑一个藏在 proxy 背后的数据库,如果有多个客户端通过 proxy 进行数据库的连接,事实上因为都是通过代理进行连接,所以数据库中的所有的操作都是 proxy 服务器的 IP 地址,这在对数据库的性能监控和优化是不利的,因为我们不知道真实异常的服务器 IP 地址。


这种情况下就需要用到 proxy protocol 协议,让数据库可以反映出真实客户端的 IP 地址,从而便于数据库的监控和问题定位。


事实上,数据库只是一个特定的例子,我们在很多其他的情况下也可能需要知道客户端真实 IP 的情况。


以现在最流行的 http 服务器和代理服务器 nginx 为例,我们来看一下如何在 nginx 中配置 proxy protocol。

proxy protocol 在 nginx 中应用

我们知道 nginx 是一个 web 服务器和代理服务器,它一般工作在 proxy server 或者负载均衡软件(Haproxy,Amazon Elastic Load Balancer (ELB)的后面。


客户端首先请求 proxy server 或者 LSB 负载均衡软件,然后再到 nginx 进行真实的 web 访问。


因为经过了多层软件,所以客户端的一些信息比如 ip 地址,端口号等可能就会被隐藏,这对于我们问题分析,数据统计都是不利的。因为对于 nginx 来说,我们希望能够获得真实的客户端 IP 地址,这样才能获取真实的请求环境。


这种情况下就需要用到 PROXY protocol 了。


如果前面所说的 proxy 或者 LSB 都实现了 PROXY protocol 协议的话,不管是 HTTP, SSL, HTTP/2, SPDY, WebSocket 还是 TCP 协议,nginx 都可以拿到客户端的原始 IP 地址,从而根据原始 IP 地址进行一些特殊的操作,比如屏蔽恶意 IP 的访问,根据 IP 不同展示不同的语言或者页面,或者更加简单的日志记录和统计等,都非常有效。


当然,如果想要支持 PROXY protocol,对 nginx 的版本也是有要求的,具体版本需求如下:


  • 想要支持 PROXY protocol v2,需要 NGINX Plus R16 或者 NGINX Open Source 1.13.11。

  • 想要支持 ROXY protocol for HTTP,需要 NGINX Plus R3 或者 NGINX Open Source 1.5.12。

  • 想要支持 TCP client‑side PROXY protocol,需要 NGINX Plus R7 或者 NGINX Open Source 1.9.3。

  • 想要支持 PROXY protocol for TCP,需要 NGINX Plus R11 或者 NGINX Open Source 1.11.4。


在 nginx 中可以通过下面的变量来获得对应的客户端信息,具体而言如下所示:


$proxy_protocol_addr$proxy_protocol_port 分别表示的是原始客户端的 IP 地址和端口号。


$remote_addr$remote_port表示的是 load balancer 的的 IP 地址和端口。


如果你使用了 RealIP 扩展模块,那么这个模块会重写$remote_addr$remote_port这两个值,将其替换成原始客户端的 IP 地址和端口号。


然后使用$realip_remote_addr$realip_remote_port来表示 load balancer 的的 IP 地址和端口。


在 RealIP 扩展模块中,$proxy_protocol_addr$proxy_protocol_port 表示的含义不变,还是原始客户端的 IP 地址和端口号。

在 nginx 中配置使用 proxy protocol

上面我们提到了 nginx 中 proxy protocol 的基本应用,下面来讲一下如何在 nginx 中进行具体的配置。

在 nginx 中启用 proxy protocol

如果你的 nginx 已经是支持 proxy protocol 的版本,那么启用 proxy protocol 非常简单,只需要在 server 中的 listen 中添加 proxy_protocol 即可,如下所示:


http {    #...    server {        listen 80   proxy_protocol;        listen 443  ssl proxy_protocol;        #...    }}   stream {    #...    server {        listen 112233 proxy_protocol;        #...    }}
复制代码


可能大家比较熟悉的是 http block,这表示的是 nginx 对 http/https 的支持。stream 模块可能大家比较陌生,这是 nginx 提供的对 tcp/udp 协议的支持。


通过上面的配置,nginx 可以实现在 tcp/udp 协议和 http/https 协议同时支持 proxy protocol。

使用 Real‑IP modules

Real‑IP modules 是 nginx 自带的一个模块,可以通过下面的命令来查看 nginx 是否有安装 real-ip 模块:


nginx -V 2>&1 | grep -- 'http_realip_module'nginx -V 2>&1 | grep -- 'stream_realip_module'
复制代码


如果你当前使用的版本没有 real ip,也不要急,这时候你可能需要从源代码进行编译。


在编译的过程中,我们需要执行一个 configure 命令,在这个 configure 命令中可以指定要开启的功能,比如 stream 或者 http_ssl_module:


$ ./configure--sbin-path=/usr/local/nginx/nginx--conf-path=/usr/local/nginx/nginx.conf--pid-path=/usr/local/nginx/nginx.pid--with-pcre=../pcre-8.44--with-zlib=../zlib-1.2.11--with-http_ssl_module--with-stream--with-mail
复制代码


如果要开启 real-ip 功能,则可以添加:


--with-http_realip_module
复制代码


如果 nginx 是运行在 SLB 或者 proxy 之后的,那么可以通过 set_real_ip_from 命令来指定代理或者负载均衡服务器的 IP 范围,如下所示:


server {    #...    set_real_ip_from 192.168.1.0/24;   #...}
复制代码


然后我们需要将 proxy 或者 SLB 的 IP 地址替换成为真实客户端的地址,那么可以这样使用:


http {    server {        #...        real_ip_header proxy_protocol;      }}
复制代码

请求转发

不管是 http 还是 stream block,都可能遇到请求向后续的 upstream 进行转发的情况,对于 upstream 来说,他们希望收到的是真实客户端 IP 地址,而不是 proxy 或者 slb 的地址,那么可以通过下面的设置来解决:


http {    proxy_set_header X-Real-IP       $proxy_protocol_addr;    proxy_set_header X-Forwarded-For $proxy_protocol_addr;}
复制代码


stream {    server {        listen 12345;        proxy_pass example.com:12345;        proxy_protocol on;    }}
复制代码


http 和 stream 的设置方式是不同的。

日志记录

日志是一个非常重要的功能,对于定位问题,执行数据统计分析都非常有用,当然我们需要的是真实的客户端 IP 地址。


我们可以通过使用变量 $proxy_protocol_addr 在 http 和 stream block 中记录对应的日志,如下所示:


http {    #...    log_format combined '$proxy_protocol_addr - $remote_user [$time_local] '                        '"$request" $status $body_bytes_sent '                        '"$http_referer" "$http_user_agent"';}
复制代码


stream {    #...    log_format basic '$proxy_protocol_addr - $remote_user [$time_local] '                      '$protocol $status $bytes_sent $bytes_received '                      '$session_time';}
复制代码

总结

通过上面的设置,nginx 已经可以使用 proxoy protocol 了,这会让我们的后续分析工作变得更加轻松。


更多内容请参考 http://www.flydean.com/02-nginx-proxy-protocol/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

发布于: 20 小时前阅读数: 15
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
在nginx中使用proxy protocol协议_Java_程序那些事_InfoQ写作社区