Skip to main content

6.接受proxy协议

本文介绍如何将 NGINX 配置为接受 PROXY 协议,将负载均衡器或代理的 IP 地址重写为 PROXY 协议标头中收到的 IP 地址,配置客户端 IP 地址的简单日志记录,以及启用 NGINX 和 TCP upstream服务器之间的 PROXY 协议。

介绍

PROXY 协议(PROXY protocol)使 NGINX 能够接收通过代理服务器和负载均衡器(如 HAproxy 和 Amazon Elastic Load Balancer (ELB))传递的客户端连接信息。

使用代理协议,NGINX可以从HTTP,SSL,HTTP / 2,SPDY,WebSocket和TCP中学习原始IP地址。了解客户端的原始 IP 地址对于为网站设置特定语言、保留 IP 地址拒绝列表或仅用于日志记录和统计目的可能很有用。

通过 PROXY 协议传递的信息是客户端 IP 地址、代理服务器 IP 地址和两个端口号。

使用此数据,NGINX可以通过多种方式获取客户端的原始IP地址:

  • 使用 $proxy_protocol_addr$proxy_protocol_port 变量捕获原始客户端 IP 地址和端口。$remote_addr$remote_port 变量捕获负载均衡器的 IP 地址和端口。
  • 使用 RealIP 模块重写 $remote_addr$remote_port 变量中的值,将负载均衡器的 IP 地址和端口替换为原始客户端 IP 地址和端口。$realip_remote_addr$realip_remote_port 变量保留负载均衡器的地址和端口,$proxy_protocol_addr$proxy_protocol_port 变量仍保留原始客户端 IP 地址和端口。

4.配置 NGINX 以接受代理协议

要将 NGINX 配置为接受 PROXY 协议标头,请将 proxy_protocol参数添加到 http upstream {} 块中的 server块中的 listen指令中。

http {
#...
server {
listen 80 proxy_protocol;
listen 443 ssl proxy_protocol;
#...
}
}

stream {
#...
server {
listen 12345 proxy_protocol;
#...
}
}

4.将负载均衡器的 IP 地址更改为客户端 IP 地址

您可以将负载均衡器或 TCP 代理的地址替换为从 PROXY 协议接收的客户端 IP 地址。这可以通过HTTPupstreamRealIP模块来完成。使用这些模块,$remote_addr$remote_port 变量保留客户端的实际 IP 地址和端口,而 $realip_remote_addr$realip_remote_port 变量保留负载均衡器的 IP 地址和端口。

要将 IP 地址从负载均衡器的 IP 地址更改为客户端的 IP 地址,请执行以下操作:

  1. 确保已将NGINX配置为接受PROXY 协议标头。请参阅配置 NGINX 以接受代理协议

  2. 确保您的NGINX安装包含HTTPStream Real-IP模块

    nginx -V 2>&1 | grep -- 'http_realip_module'
    nginx -V 2>&1 | grep -- 'stream_realip_module'

    如果没有,请使用这些模块重新编译NGINX。

  3. HTTPstreamset_real_ip_from指令中,指定 TCP 代理或负载均衡器的 IP 地址或 CIDR 地址范围:

    server {
    #...
    set_real_ip_from 192.168.1.0/24;
    #...
    }
  4. http {}上下文中,通过指定 real_ip_header 指令的 proxy_protocol参数,将负载均衡器的 IP 地址更改为从 PROXY 协议标头接收的客户端的 IP 地址:

    http {
    server {
    #...
    real_ip_header proxy_protocol;
    }
    }

记录原始 IP 地址

知道客户端的原始 IP 地址后,可以配置正确的日志记录:

对于 HTTP,将 NGINX 配置为使用 $proxy_protocol_addr 变量和 proxy_set_header 指令将客户端 IP 地址传递给upstream服务器:

http {
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_protocol_addr;
}

$proxy_protocol_addr 变量添加到 log_format指令(HTTPStream):

#在http块中:
http {
#...
log_format combined '$proxy_protocol_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
}

#在stream块中:
stream {
#...
log_format basic '$proxy_protocol_addr - $remote_user [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time';
}

用于与上游的 TCP 连接的代理协议

对于TCP流,可以为NGINX和upstream服务器之间的连接启用PROXY协议。要启用 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 前面有一个负载均衡器来处理所有传入的 HTTPS 流量,例如 Amazon ELB。NGINX 接受端口 443 (listen 443 ssl) 上的 HTTPS 流量,端口 12345 上的 TCP 流量,并接受通过 PROXY 协议从负载均衡器传递的客户端 IP 地址(http {}stream {}块中 listen指令的 proxy_protocol参数)。

NGINX终止HTTPS流量(ssl_certificatessl_certificate_key指令)并将解密的数据代理到后端服务器:

  • 对于 HTTP:proxy_pass http://backend1;
  • 对于 TCP:proxy_pass backend.example.com:12345

它包括客户端 IP 地址和端口以及 proxy_set_header 指令。

log_format 指令中指定的 $proxy_protocol_addr 变量还将客户端的 IP 地址传递给 HTTP 和 TCP 的日志。

此外,TCP 服务器(stream {}块)将自己的 PROXY 协议数据发送到其后端服务器(proxy_protocol on指令)。