前言
在现代开发中有很多场景需要拿到用户的真实ip,比如安全策略,和地区热点信息推送等功能,但是现在代理很多。用户可能会通过代理访问服务,或者黑客攻击的时候也会使用很多肉机隐藏其真实ip。接下来我们就来一起学习一下看怎么才能拿到真实的ip。
一、proxy protocol协议
proxy protocol 是一种由 haproxy 作者 willy tarreau 于 2010 年设计的网络协议,旨在解决代理环境中后端服务器无法获取客户端真实信息(如源 ip、端口等)的问题。它通过在 tcp/udp 连接建立后插入一个轻量级头部,实现客户端信息的透传,且与上层应用协议无关
它有两个版本:v1、v2
- v1
- 结构: proxy <协议类型> <源ip> <目标ip> <源端口> <目标端口>
特点:- 人类可读,便于调试。
- 仅支持 tcp/ipv4/ipv6,无扩展能力。
- 头部最大长度 108 字节(ipv6 场景)
- v2
字段 | 长度 | 说明 |
---|---|---|
签名(signature) | 12 字节 | 固定值 0x0d0a0d0a000d0a515549540a,标识 v2 协议 |
版本/命令(ver/cmd) | 1 字节 | 高 4 位为版本号(0x2),低 4 位为命令(0x1=proxy;0x0=local) |
地址族/协议(fam) | 1 字节 | 高 4 位为地址族(ipv4/ipv6/unix),低 4 位为协议(tcp/udp) |
数据长度(len) | 2 字节 | 后续数据的长度(大端序) |
地址数据(addr) | 可变 | 根据地址族存储源/目标 ip 和端口 |
tlv 扩展 | 可变 | 类型-长度-值结构,传递 ssl 证书、唯一 id 等扩展信息 |
特点:
- 高效:二进制解析速度快,适合高并发场景
- 可扩展:通过 tlv 支持 ssl 信息(如证书 cn、加密算法)、唯一连接 id 等
- 多协议:支持 udp、unix socket 等
二、配置方法
现在内网测试有两台机器:
一台70,一台80;两台均为linux机器
其中70是用于代理转发,把请求转发到80机器上进行处理
80就是部署程序的机器了
代理服务器配置
这里也就是指的70服务器
首先找到nginx对应的配置文件。一般路径都在:etc目录下
先切换到管理员权限:
sudo su
编辑配置文件命令
vi /etc/nginx/nginx.conf
配置内容
http模块代理
http { # 使用 http 模块处理 http 流量 server { listen 8888 proxy_protocol; # 启用 proxy protocol 解析 server_name localhost; location / { proxy_pass http://192.168.1.80:7890/;#转发目标地址(应用程序部署服务器) # 传递真实 ip 到后端(需后端支持) proxy_set_header x-real-ip $proxy_protocol_addr; # 真实客户端 ip proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; # 兼容性头部 proxy_set_header host $host; } } log_format tcp_log 'http->[$time_local] $proxy_add_x_forwarded_for → $server_addr ($status)'; access_log /var/log/nginx/tcp_access.log tcp_log;#记录日志 }
防火墙开放端口
sudo ufw allow 8888/tcp
stream 模块代理
这我配置了一下日志,方便后面测试查看配置是否起效
stream { server { listen 8880 proxy_protocol; # 启用proxy协议解析 proxy_pass 192.168.1.80:7890;#转发目标地址(应用程序部署服务器) proxy_protocol on; #显示启动代理协议透传 } log_format tcp_log 'tcp->[$time_local] $proxy_protocol_addr → $server_addr ($status)'; access_log /var/log/nginx/tcp_access.log tcp_log;#记录日志 }
防火墙开放端口
sudo ufw allow 8880/tcp
注意
http模块代理和stream模块代理监听的端口不能是相同的
修改配置文件后需要重启nginx
nginx -s reload
重启服务
systemctl restart nginx
在stream 模块中的这一段配置
listen 8880 proxy_protocol
这里如果设置了就需要在请求中携带prox头,下面我会用c#代码示例怎么携带,这一般是代理服务器自己会处理的,如果连接70机器的服务没有使用代理服务器这里我们就可以不添加proxy_protocol
这一段 直接使用listen 8880
即可,这样普通的tcp请求也可以直接连接。
测试配置是否生效
端口检查
重启成功后我们可以通过以下命令查看端口是否正常监听
sudo netstat -na | grep list | grep 8888
如果查看正常监听那就是配置成功了
测试ip记录
验证http
linux命令行输入以下命令(这里测试用的内网85的机器发起请求)
curl -h “x-forwarded-for: 203.0.113.42” 192.168.1.70 8888
日志打印内容如下:
http->[22/jul/2025:09:40:16 +0800] 203.0.113.42, 192.168.1.85 → 192.168.1.73 (200)
验证tcp
这里验证tcp我是采用的.net core代码发起的tcp请求(181是我本机ip)
代码如下:
static void main(string[] args) { try { // 创建 socket socket socket = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp); // 连接到nginx ipaddress ipaddress = ipaddress.parse("192.168.1.73"); ipendpoint remoteep = new ipendpoint(ipaddress, 8880); socket.connect(remoteep); // 构造并发送proxy protocol v1头部 string proxyheader = $"proxy tcp4 {getlocalip()} 192.168.1.73 {getlocalport(socket)} 8880\r\n"; byte[] proxyheaderbytes = encoding.ascii.getbytes(proxyheader); socket.send(proxyheaderbytes); // 发送实际数据 string request = "123465"; byte[] requestdata = encoding.ascii.getbytes(request); socket.send(requestdata); // 辅助函数:获取本地ip string getlocalip() { return dns.gethostentry(dns.gethostname()) .addresslist.first(ip => ip.addressfamily == addressfamily.internetwork) .tostring(); } // 辅助函数:获取本地端口 int getlocalport(socket _socket) { return ((ipendpoint)_socket.localendpoint).port; } } catch (exception ex) { console.writeline($"发生错误: {ex.message}"); } }
日志打印内容如下
tcp->[22/jul/2025:10:16:32 +0800] 192.168.1.181 → 192.168.1.73 (200)
这里我们就可以看到我们模拟的proxy头里的ip了
流程如下
注意事项和理解误区
对于用户的真实ip获取需要注意的是,http请求可以从x-forwarded-for里获取,但是tcp请求就需要请求发起方携带prox头,不然nginx会返回400错误。
应用程序机器配置
程序中可以使用suerpsocket包,supersocket 是一个用于 .net 的高性能、可扩展的套接字服务器应用程序框架。它为构建自定义网络通信应用程序提供了强大的架构,支持包括 tcp、udp 和 websocket 在内的多种协议。它2.0中还支持proxyprotocol协议的支持。
代码在程序中的位置:supersocket.connection/connectionbase
总结
以上就是nginx配置proxy protocol代理获取真实ip的全过程的详细内容,更多关于nginx配置proxy protocol获取真实ip的资料请关注代码网其它相关文章!
发表评论