本文介绍了如何配置 NGINX 和 NGINX Plus 以接受 PROXY 协议,将负载平衡器或代理的 IP 地址重写为在 PROXY 协议头中接收到的 IP 地址,配置客户端 IP 地址的简单日志记录,启用 NGINX 和 TCP upstream 服务器之间的 PROXY 协议。
PROXY 协议允许 Nginx 和 Nginx Plus 接受来自代理服务器和负载平衡器的客户端连接信息,比如 HAproxy 和 Amazon Elastic Load Balancer (ELB)
通过 PROXY 协议,Nginx 可以从 HTTP,SSL,HTTP / 2,SPDY,WebSocket 和 TCP 中获取到源 IP 地址。获取到客户端的源 IP 地址,可以在为网页指定语言、设置 IP 黑名单或只是简单的日志和统计分析。
通过 PROXY 协议传输的数据是客户端的 IP 地址、代理服务器的 IP 地址和所有的端口号。Nginx 可以通过这个数据使用几种不同方法获取到客户端的源 IP 地址:
$proxy_protocol_addr
和 $proxy_protocol_addr_port
变量保存客户端源 IP 地址和端口。$remote_addr
和 $remote_port
变量保存负载平衡服务器的 IP 和端口。$remote_addr
和 $remote_port
变量从负载平衡器的IP和端口重写为原始客户端 IP 地址和端口。 $realip_remote_addr
和 $realip_remote_port
端口变量将保留负载均衡器的地址和端口,$proxy_protocol_addr
和 $proxy_protocol_port
变量将始终保留原始客户端 IP 和端口。ngx_http_realip_module
和 ngx_stream_realip_module
模块,详情可以查看 安装 Nginx 开源版本。对于 Nginx Plus,不需要额外的安装步骤。要配置 Nginx 接受 PROXY 协议头,请将 proxy_protocol
参数添加到 http 或 stream 的 listen
指令中:
http {
...
server {
listen 80 proxy_protocol;
listen 443 ssl proxy_protocol;
...
}
}
对 TCP stream 流的配置:
stream {
...
server {
listen 12345 proxy_protocol;
...
}
}
现在可以使用表示客户端 IP 地址和端口的 $proxy_protocol_addr
和 $proxy_protocol_port
变量,另外还可以配置 HTTP realip 或 stream realip 模块以将负载均衡器的 IP 替换为 $remote_addr
和 $remote_port
变量中的客户端 IP。
可以将负载均衡器的 IP 地址改为从 PROXY 协议接收到的客户端 IP 地址。这可以通过 ngx_http_realip_module
和 ngx_stream_realip_module
模块来实现。通过这些模块,$remote_addr
和 $remote_port
变量保存客户端源 IP 地址和端口。$realip_remote_addr
和 $realip_remote_port
变量保存负载平衡服务器的 IP 和端口。
nginx -V 2>&1 | grep -- 'http_realip_module'
nginx -V 2>&1 | grep -- 'stream_realip_module'
如果没有这些模块,可以编译包含这些模块的 Nginx,参考这里。
set_real_ip_from
指令指定 TCP 代理或负载均衡器的 IP 地址或 CIDR 地址范围:server {
...
set_real_ip_from 192.168.1.0/24;
...
}
real_ip_header
指令中,指定 proxy_protocol
参数:server {
...
real_ip_header proxy_protocol;
}
当知道客户端的原始 IP 地址时,可以配置正确的日志记录:
通过 proxy_set_header
指令和 $proxy_protocol_addr
变量,可以把 Nginx 的客户端 IP 地址发送到 upstream 服务器:
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_protocol_addr;
对于 http 或 stream,可以使用 log_format
指令和 $proxy_protocol_addr
变量:
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';
}
对于 TCP 数据流,可以开启在 Nginx 和一台上游服务器(upstream server)之间的 PROXY 协议。在 stream{} 上下文中的 server 块中添加 proxy_protocol
指令即可:
stream {
server {
listen 12345;
proxy_pass example.com:12345;
proxy_protocol on;
}
}
http {
log_format combined '$proxy_protocol_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
...
server {
server_name localhost;
listen 80 proxy_protocol;
listen 443 ssl proxy_protocol;
ssl_certificate /etc/nginx/ssl/public.example.com.pem;
ssl_certificate_key /etc/nginx/ssl/public.example.com.key;
location /app/ {
proxy_pass http://backend1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_protocol_addr;
}
}
}
stream {
log_format basic '$proxy_protocol_addr - $remote_user [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time';
...
server {
listen 12345 ssl proxy_protocol;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/cert.key;
proxy_pass backend.example.com:12345;
proxy_protocol on;
}
}
这个例子假设 Nginx 前面有负载平衡器(例如,亚马逊 ELB)平衡所有传入的 HTTPS 流量。Nginx 接受端口 443 的 HTTPS 流量,端口 12345 的 TCP 流量,并接受 PROXY 协议(http {} 和 stream {} 块中的 listen 指令中的 proxy_protocol 参数)。
Nginx 终止 HTTPS 流量(ssl_certificate
和 ssl_certificate_key
指令),将解密后的数据发送到包括客户端的 IP 地址和端口(proxy_set_header
指令值)的后台服务器(对于http {} 使用 proxy_pass http://backend1;
,对于 stream {} 使用 proxy_pass backend.example.com:12345
)。
log_format
指令中指定的 proxy_protocol_addr
变量也会将客户端 IP 地址传给 http {} 和 stream {} 块中的日志。
此外,TCP 服务器(stream {} 块)发送自己的 PROXY 协议的流量到后端服务器(proxy_protocol 指令设置为 on)。