-
代理和获取规范
-
原则
- F5第一层代理,Nginx作为第二层代理,最终到web应用,如示例:F5->Nginx->Web
- 第一层代理需要把源IP设置到X-Real-IP,二级代理需把一级代理地址写入X-Forwarded-For
-
名词解释
- X-Real-IP: 自定义请求头中字段
- X-Forwarded-For:一个 HTTP 扩展头部,各大 HTTP 代理、负载均衡等转发服务广泛使用,并被写入 RFC 7239(Forwarded HTTP Extension)标准之中
-
定义规范的背景
- X-Real-IP和X-Forwarded-For都可以被客户端随意伪造,但是自定义X-Real-IP只能存在一个值,X-Forwarded-For字段可以存在多个以逗号分割,代理服务器只能追加,不能确定源IP地址写入的具体顺序
- Remote Address 无法伪造,因为建立 TCP 连接需要三次握手,如果伪造了源 IP,无法建立 TCP 连接,更不会有后面的 HTTP 请求
- 基于以上2点,需要一级代理服务器把源IP写入X-Real-IP,各级代理需把上级代理地址写入X-Forwarded-For中,追踪代理调用链
-
F5地址转换规范
- 在F5配置界面中,把源 IP写入参数: X-Real-IP
-
Nginx地址转换配置模板:
Nginx配置server {# 监听端口listen 7777;server_name localhost;charset utf-8;location /{# 转发地址proxy_pass http://10.19.205.58:7777;proxy_set_header Host $host;# 只有一层代理时候开启,源地址放入X-Real-Ip# proxy_set_header X-Real-IP $remote_addr;# Nginx作为代理必须开启,源地址和代理服务器代理地址放入X-Forwarded-For字段proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}} -
java代码获取源地址
publicString getRealIp(HttpServletRequest request){// 1. 获取真实地址String realIp = request.getHeader("X-Real-IP");// 2. 非正常地址或者异常调用if(isIlegalHost(realIp)) {// 2.1 获取X-Forwarded-For地址组String forwardIps= request.getHeader("X-Forwarded-For");if(forwardIps !=null){String[] forwardIpArray = forwardIps.split(",");// 2.2 循环X-Forwarded-For 的地址,返回第一个正常的地址for(String forwardIp : forwardIpArray){if(!isIlegalHost(forwardIp)){returnforwardIp;}}}//2.3 X-Forwarded-For不存在正常地址,则取远程地址realIp = request.getRemoteAddr();// 防止Nginx和web同一台无法获取地址if("127.0.0.1".equals(realIp)) {try{// 2.4 如果是本机,则尝试获取网卡配置的IPrealIp = InetAddress.getLocalHost().getHostAddress();}catch(UnknownHostException e) {}}}returnrealIp;}publicbooleanisIlegalHost(String ip){if(ip ==null|| ip.length() ==0||"unknown".equalsIgnoreCase(ip)) {returntrue;}returnfalse;}
-
2.场景分析
-
-
场景一:Nginx->Web;F5->Web
- F5只需界面操作,配置源地址写入:X-Real-IP
-
Nginx配置
server {# 监听端口listen 7777;server_name localhost;charset utf-8;location /{# 转发地址proxy_pass http://10.19.205.58:7777;proxy_set_header Host $host;# 源地址放入X-Real-Ipproxy_set_header X-Real-IP $remote_addr;# 调用地址增加到X-Forwarded-For字段proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}} -
实例结果:
# 访问路径:http://10.19.205.60:7777/express# 客户端地址:10.20.23.50# Nginx地址:10.19.205.60# web应用地址:10.19.205.58# 代码:request.getRemoteAddr()+"|"+request.getHeader("X-Real-IP")+"|"+ request.getHeader("X-Forwarded-For")+"|"+getRealIp();# 返回结果:Remote Addr:10.19.205.60X-Real-IP:10.20.23.50X-Forwarded-For:10.20.23.50getRealIp():10.20.23.50 - 异常情况分析
-
异常情况:直接调用web地址,解决方法:java示例代码行注释2
# 返回结果:Remote Addr:10.20.23.50X-Real-IP:nullX-Forwarded-For:nullgetRealIp():10.20.23.50
-
-
场景二:F5->Nginx->Web
-
F5只需界面操作,配置源地址写入:X-Real-IP
- Nginx配置:
server {# 监听端口listen 7777;server_name localhost;charset utf-8;location /{# 转发web应用地址proxy_pass http://10.19.205.58:7777;proxy_set_header Host $host;# F5调用地址增加到X-Forwarded-For字段proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}} - 实例结果:
# 访问路径:http://10.19.18.51:7777/express# 客户端地址:10.20.23.50# F5虚拟地址:10.19.18.51# Nginx地址:10.19.205.59# web应用地址:10.19.205.58# 代码:request.getRemoteAddr()+"|"+request.getHeader("X-Real-IP")+"|"+ request.getHeader("X-Forwarded-For")+"|"+getRealIp();# 返回结果:Remote Addr:10.19.205.59X-Real-IP:10.20.23.50X-Forwarded-For:10.19.18.254(F5实际地址)getRealIp():10.20.23.50 -
异常情况分析
-
异常情况一:直接调用nginx地址,解决方案:java示例代码行注释2
# 返回结果:Remote Addr:10.19.205.59X-Real-IP:nullX-Forwarded-For:10.20.23.50getRealIp():10.20.23.50 -
异常情况二:直接调用web地址,解决方法:java示例代码行注释2.3
# 返回结果:Remote Addr:10.20.23.50X-Real-IP:nullX-Forwarded-For:nullgetRealIp():10.20.23.50
-
-
-
Nginx->Nginx->Web
-
第一层 Nginx配置:
server {# 监听端口listen7777;server_name localhost;charset utf-8;location /{# 转发web应用地址proxy_pass http://10.19.205.58:7777;proxy_set_header Host $host;# 源地址放入X-Real-Ipproxy_set_header X-Real-IP $remote_addr;# 调用地址增加到X-Forwarded-For字段proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;} -
第二层 Nginx配置:
server {# 监听端口listen7777;server_name localhost;charset utf-8;location /{# 转发web应用地址proxy_pass http://10.19.205.58:7777;proxy_set_header Host $host;# 第一层调用地址增加到X-Forwarded-For字段proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}} -
实例结果:
# 访问路径:http://10.19.205.69:7777/express# 客户端地址:10.20.23.50# 第一层Nginx地址:10.19.205.69# 第二层Nginx地址:10.19.205.59# web应用地址:10.19.205.58# 代码:request.getRemoteAddr()+"|"+request.getHeader("X-Real-IP")+"|"+ request.getHeader("X-Forwarded-For")+"|"+getRealIp();# 返回结果:Remote Addr:10.19.205.59X-Real-IP:10.20.23.50X-Forwarded-For:10.20.23.50,10.19.205.69getRealIp():10.20.23.50
- 异常情况分析
-
异常情况一:直接调用第二层nginx地址,解决方案:java示例代码行注释2
# 返回结果:Remote Addr:10.19.205.59X-Real-IP:nullX-Forwarded-For:10.20.23.50getRealIp():10.20.23.50 -
异常情况二:直接调用web地址,解决方法:java示例代码行注释2.3
# 返回结果:Remote Addr:10.20.23.50X-Real-IP:nullX-Forwarded-For:nullgetRealIp():10.20.23.50
-
-
-
