一 指令学习
① set_real_ip_from
解读:表示这些是来自'代理服务器的ip [受信的]',不是用户的'真实ip'大白话理解: 如果发现与nginx建连的是这些'受信ip',则可以通过特殊方式获取'client_ip';否则则'不处理'
细节点: 把set_real_ip_from xxx '放在server{}下','不要'放在location{}中原因: realip工作在'find_config阶段之前'
++++++++++ 关于设置为'CIDR'场景的理解 ++++++++++场景1:real_client --> '中间层' -> 'SNAT源地址转换' --> 替换为'CIDR地址池'中的'某个ip' --> 导致ip一直'发生变化',所以要'使用网段'场景2:'CDN的ip'比较多,可以用'子网掩码',把一段ip设为'可信地址'的
CDN 获取客户端的真实ip
② real_ip_header
+++++++++++++"指令解读"+++++++++++++1) 则是告诉'nginx' 从'哪里[重点]'获取真正的'client_ip'和'client_port'2) 找到之后,然后替换'$remote_addr'和'$remote_port'
方式1: '自定义field'、'X-Real-IP'、'X-Forwarded-For' 请求头 -->'七层'
PROXY protocol
方式2: proxy_protocol -->'四层'++++++++ "前置条件" ++++++++ 目的: 通过在 'listen 指令'中设置 proxy_protocol 参数来'启用 PROXY 协议'listen 443 ssl proxy_protocol;listen 8443 proxy_protocol;细节点: 在一个'server'块中,可以同时设置'多个'listen
listen指令
③ real_ip_recursive
对于这个指令的讨论
real_ip_recursive 是否'递归地排除'直至得到'用户ip'1)off :会将 real_ip_header 指定的HTTP头中的'最后一个ip'作为真实ip2)on :会将 real_ip_header 指定的HTTP头中的'最后一个不是信任服务器的ip'当成真实ip
1)首先,real_ip_header 指定一个'http首部'名称,默认是X-Real-Ip2) 假设用'默认值'的话,nginx在'接收'到报文后,会查看http首部X-Real-Ip[1]、X-Real-Ip是'1个值'如果有1个ip,它会去核对,'发送方的ip'是否在set_real_ip_from'指定的信任ip列表'中;如果是'被信任'的,它会去认为这个X-Real-Ip中的ip值是'前代理'告诉自己的-->用户的真实ip值于是,它会将该值'赋值'给自身的$remote_addr变量;如果'不被信任',那么将'不作处理',那么$remote_addr还是'发送方'的ip地址;[2] 如果X-Real-Ip有'多个'ip值比如'代理'是这么设置的:proxy_set_header X-Real-Ip $proxy_add_x_forwarded_for;得到的是'一串IP',那么此时real_ip_recursive 的值就'至关重要'了;nginx将会从ip列表的'右到左',去比较set_real_ip_from 的'信任列表中的ip';1、如果real_ip_recursive为'off',那么当'最右边'一个ip,发现是信任ip,即认为下一个IP(右边第二个)就是用户的真正IP;2、如果real_ip_recursive为'on',那么将从'右到左'依次比较,直到找到一个'不是信任ip'为止,然后同样把IP值复制给$remote_addr。
案例讲解
④ 变量
三 知识铺垫
(1)相关内置变量
① $remote_addr
强调: 这里的'客户端'指的是'直接请求nginx'的客户端,非'origin_client_ip'补充: $remote_addr是获取的是'直接tcp连接'的客户端IP,这个是'无法伪造'的通俗: 与nginx建立tcp连接的'对端ip'地址
② $proxy_add_x_forwarded_for
+++++++++ "解读" +++++++++1) 如果请求到'nginx',没有携带'X-Forwarder-For'请求头,则$proxy_add_x_forwarded_for'记录'的是'$remote_addr'2) 如果请求到'nginx',并且'携带X-Forwarder-For'请求头,则$proxy_add_x_forwarded_for除了记录"X-Forwarder-For"头的值,还会将'$remote_addr'追加末尾,以"逗号分割"X-Forwarded-For: client1, proxy1, proxy2+++++++++ "常用" +++++++++proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
伪造XFF
1)client '自定义' X-Forwarded-For: 'false_client1', 'proxy1'2)server 设置 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;3)导致upstream_server'上游服务器'获取的是X-Forwarded-For的第一个ip,也即'false_client'
③ $proxy_protocol_addr $proxy_protocol_port
说明: 通过'proxy protocal'中解析出来'client_real_ip'和'client_real_port'备注: 这两个变量是'core'提供的
(2)前置条件
① nginx编译realip模块
七层: 默认"源码编译"情况下是'该参数不被'编译,应使用 --with-http_realip_module 配置参数'启用'它
+++++++++++ "未编译这个参数使用该模块的指令会报错" +++++++++++
[emerg] unknown directive "set_real_ip_from" in /etc/nginx/nginx.conf:行号
② nginx 对 proxy protocol协议支持
代理协议:分为v1和'v2'两个版本,v1是人类易读的,'v2是二进制'格式的应用场景:一般上层作为'tcp'代理,'代理层(send方)'需要支持'Proxy protocol',才能在'TCP报文中添加特殊信息',同时'nginx也需要支持'Proxy protocol,才能'解析'
重点: 了解'云厂商'如何实现的
阿里云 通过Proxy Protocol获取客户端真实IP(四层监听)
haproxy作者 关于PROXY protocol协议解读
AWS 关于 PROXY protocol解读
nginx 关于 proxy protocol的使用
Proxy Protocol support software
华为云 VPCEP 关于 Proxy Protocol
华为云 WAF 获取客户端真实ip
(3) PROXY protocol
① 引入
② 拓扑理解
③ 协议格式
④ 抓包分析
⑤ 思考
+++++++ 七层'为什么'使用Proxy protocol +++++++1)客户端没有'XFF、X-Real-Ip'头,你不能要求'client'做改变2)XFF头等可能被'伪造',不具备'可考'性四层使用'Proxy protocol'原因: 不具备解析'HTTP'七层的能力
四 案例讲解
(1)案例一
+++++++++ "X-Forwarded-For应用" +++++++++1)一般'CDN'厂商是通过这种方式获取2)这里讲解的是'nginx'获取客户端的'real_ip'方法; 'web应用(tomcat、django)'也可以处理
① 请求链路
client('172.25.2.157') -> proxy1('172.25.2.157') -> server('172.25.2.100')
② 默认
③ 对比实验1
细节点: 观察'$remote_addr'
④ 对比实验2
理解: 开启回溯之后,nginx认为'可信的地址'都是'proxy',所以继续'往前'追溯,直到找到一个不是'可信'的地址,认为是'real_ip'
(2)案例二
① 应用场景
② 配置
需求: 配置Proxy Protocol获取'源地址'功能,本次实验利用'http'而非'stream'模块
③ 客户端测试
④ 扩展
四层(haproxy) + 七层(nginx) + PROXY protocol获取客户端的真实ip
haproxy的Proxy Protocol代理协议
注意点:'PROXY protocol'是端口级别;作用所有相同端口,可能'导致报错'
参考地址
(3)关于兼容性和日志的思考
说明: 很多时候,访问线路不一样,导致获取'client_real_ip'的方式不一样,导致'日志'记录方式不一样
① 端口
+++++++++ "多条线路通过端口区分" +++++++++server {listen 443 http2; # 兼容http1.1listen 6443 ssl proxy_protocol;...}+++++++++ "补充其它细节" +++++++++server {listen 443 ssl http2;
}特点: 上述配置只支持'http2'协议,不兼容http1现象: curl发送请求时,'直接返回的是http/2',而'不是'http/1.1原因: 虽然服务端配置了'只使用http2',但万一'客户端未支持'http2协议,直接返回http2客户端会'解析不了'
② 日志格式
关于map的知识铺垫
++++++++++++ "假定有两条解析线路" ++++++++++++1)线路1 -->通过'waf(web应用防火墙-->也是一层代理)'到nginx备注:不同的'云厂商'waf透传源ip的方式'(管理面和租户面还不太一样)'不一样,一般有'自定义http变量[本次讲解]'、'XFF'头方式传递2)线路2 -->通过'haproxy、vpcep [支持PROXY protocol]'到nginxmap $proxy_protocol_addr $client_real_ip {"" $middle_real_ip;default $proxy_protocol_addr;
}map $http_waf_real_ip $middle_real_ip {"" $remote_addr;default $http_waf_real_ip;
}日志格式的'重点': X-Real-IP=[$client_real_ip]+++++++++ '分割线' +++++++++ 如何'递归'解析: 使用$client_real_ip --> '使用'的时候 --> $middle_real_ip1)map指令对应的'结果变量'只有在'之后的配置文件'中'使用'到了该结果变量的时候2)才会'使用前面'定义的map模块('有该变量的')来进行映射
四 遗留
client --> waf('规则清洗') --> lvs(dr) --> nginx 获取客户端的真实ip -->什么变量透传的client --> lvs(fullnat) --> nginx toa获取客户端的真实ip
我的'上层4层代理服务器'如果要获取这个连接的'真实'地址,也需要在收到'tcp报文时解析proxy_protocol 协议'client --> lvs(fullnat) --> nginx 不装toa模块,获取的是什么限速、限流、做统计
投票系统,防止刷票,需要限制一个IP只能投票一次 -->客户端真实ip的应用场景++++++ 400报错场景 ++++++
1)Bad Request(Invalid Hostname) HTTP Error 400$proxy_host --> 默认 proxy_set_header --> invalid host -->后端服务报错'400'根因:长连接导致,全局的'proxy_set_header'被覆盖2)双向认证,客户端证书过期3)请求头太大request header过大所引起,request过大,通常是由于cookie中写入了较大的值所引起https://www.cnblogs.com/duanweishi/p/9286461.html400是一种是HTTP状态码,400 Bad Request。是在打开网页时浏览器返回到客户端的一种状态码。显示在客户端的也就是400页面。
400页面是当用户在打开网页时,返回给用户界面带有400提示符的页面。其含义是你访问的页面域名不存在或者请求错误。
主要有两种形式:
1、bad request意思是“错误的请求";
2、invalid hostname意思是"不存在的域名”思考: nginx的'排查'思路