Linux IPv4 forward自动丢弃源IP为系统已绑定的IP的问题

A是路由,其上做了策略,一部分符合特定条件的流量,是要转发到B进行封装处理,A和B之间做了数个VLAN,B根据VLAN应用不同的策略。

不过这里的问题是,B的默认网关也是A(环路问题是不存在的,已经有对应规则处理),所以B自身发出的流量也会由A应用策略,转发回给B自己。而B收到src IP是local IP(系统已绑定的IP)的包时,kernel会直接丢弃数据包,所以除了B自己的数据包,B都能正常封装。

解决这个问题的一个方法,是让A在把数据包转发给B前,做一次SNAT,这样B收到的数据包,src IP就是A的IP了。其实SNAT并没有什么影响,A对NAT也有hardware offload,但这样操作实在觉得多此一举。

翻了下kernel ip sysctl部分的文档,发现个参数accept_local:

accept_local – BOOLEAN

Accept packets with local source addresses. In combination with suitable routing, this can be used to direct packets between two local interfaces over the wire and have them accepted properly. default FALSE

所以把这个参数置1就能不依赖SNAT解决问题了:

IPv4 CIDR/NETMASK的简单计算

平时购买独立服务器,机房给的IP,大都以IP/CIDR或者IP/NETMASK来表示,应该很多人第一次看到都不懂是什么意思,我当初也如此……

下文以192.168.1.0/255.255.255.0为例:

本例的IP段是IP/NETMASK格式的,要计算IP的数目,个人建议首先把子网掩码(NETMASK)转换为CIDR。

互相转换其实也不难,例如:255.255.255.0这个子网掩码,把255转换为二进制,你就会发现刚刚好是八个“1”:

255

因此255.255.255.0的CIDR=3*8=24,子网掩码最多四个255,CIDR最大也就是32。

至于子网掩码和CIDR更多有关的介绍的不是本文重点,我就不继续做文章了,需要的可以去看看维基百科。

得到了CIDR的值,算起来就简单了。

IP数=2^CIDR最大值-该IP段CIDR值

例如前面所说的255.255.255.0,经过计算,CIDR值为24,那IP数目就是2的32减24次方=2^8=256个。

然后取IP的最后一位,也就是0,+256-1=255,那么192.168.1.0/255.255.255.0的首个IP是192.168.1.0,末IP是192.168.1.255。为什么要减一?别忘了192.168.1.0也是个IP啊……

如果最后得数超过255的话,就取余数,IP的倒数第二位进一,最后一位归零,再把余数加到最后一位,超过255的话就继续……

知道首IP和末IP后,就可以计算出Network,Gateway和Broadcast,一般Network都是首IP,本例中为Network为192.168.1.0,Gateway一般为第二个IP,本例中为192.168.1.1,Broadcas一般为末IP,本例中为192.168.1.255。

三个IP被占了,那么最后剩下的可用IP数目为253(192.168.1.2-192.168.1.254)。

使用Varnish Cache时,让Nginx获取访客真实IP

之前发表过一篇文章:Varnish(前)+Nginx(中)时,让Apache(后)获取用户真实IP(多重代理)

该方法能成功解决使用或者不使用CDN时,在Varnish前端,Nginx中端,Apache解析PHP文件的情况下让Apache获取访客真实IP。因为当时主要是利用Nginx进行缓存,没使用其他功能,旧没让Nginx也获取访客真实的IP。

其中Varnish处理XFF的关键代码:

也就是把客户的IP赋值给XFF。那样后端处理就方便多了。

昨天在为一台Varnish+Nginx+Apache的服务器添加并测试防轻量级CC的功能,使用的Nginx模块是limit_req。虽然访问网站时,能获取访客的正常IP,但是Nginx得到的却是Varnish所在的服务器的IP。

测试时发现,由于limit_req模块是在Nginx上的,那样受到CC攻击的话,Nginx就会误判断是Varnish服务器的IP发出的请求,就会Ban掉Varnish服务器的IP,再次遇到Varnish的IP时,就会返回403……

403

 

尝试了Nginx的realip_module,发现无任何效果。

于是在另一台空闲的VPS上安装了Ubuntu 12.04,安装了Varnish,Nginx,PHP5-FPM进行测试。

研究了下Varnish和Nginx的配置,于是写了和PHP程序,查看XFF等信息:

测试后发现,REMOTE_ADDR一直都是Varnish所在的服务器的IP,而limit_req获取的正是REMOTE_ADDR的内容,那就是Ban Varnish的IP的原因了。由于Varnish那设置了HTTP_X_FORWARDED_FOR的内容舍弃原有内容(例如CDN服务器传来的),并且只含有客户IP,因此该项正常且不会影响REMOTE_ADDR。X-Real-ip这项内容是空白的,使用realip_module时,会使用到real_ip_header X-Real-IP这代码,大概是告诉服务器X-Real-IP才是客户真正的IP吧。

这下目标明确,要么搞定REMOTE_ADDR的,要么让real_ip_header X-Real-IP的real_ip_header获取真实的IP。

在Varnish那尝试了几次把客户的IP赋值给REMOTE_ADDR,使用或者不使用CDN时,REMOTE_ADDR就出问题了,要么是客户IP,要么CDN服务器的IP。

想了想,既然Nginx的real_ip_header能让Nginx知道访客真实IP,而HTTP_X_FORWARDED_FOR只有访客IP,把HTTP_X_FORWARDED_FOR的内容赋值给real_ip_header不就可以了么?

于是在删除原来所有与real_ip_header有关的代码,在Nginx的网站配置的server层(注意是server不是location)加入如下代码:

果然立竿见影,再次访问那个PHP文件,不管前面有没有上CDN,REMOTE_ADDR都与XFF的内容一模一样,也就是说,Nginx能获取到访客真实IP了。

把刚刚的成果帮到LANVMP的服务器上,在本机上狂F5,返回403,这是预料之中的事情,关键是,别的访客是不是200。于是再次打开17ce进行检查,非常好,全部都是200状态:

200

 

虽然解决该问题花了很多时间去折腾,不过能完美解决,也值得了……

Varnish(前)+Nginx(中)时,让Apache(后)获取用户真实IP(多重代理)

不得不说我有点问题,网站一个Apache+Varnish不够,还要在Apache和Varnish之间插入个第三者——Nginx。

因为某些需要,也给该服务器上的个别网站上了CDN。
Apache做后端,前面多一个Nginx Proxy Cache+Varnish,获取用户的真实IP不难,只需给Apache上一个rpaf模块就行了,然后让Varnish处理XFF的内容就好了:

无CDN的情况下访问网站,可以正确获取到用户的真实IP。

最近给网站上多了一个CDN,rpaf模块就显得无能为力了。因此,这几天一直苦恼如何让我的网站获取用户真实IP。
问了下那家CDN的客服,给了我这个回答:
当使用CDN后,网站服务器访问日志中的IP地址都将记录为CDN服务器的官方IP。您可以通过下列方式获取访问用户的原始IP:
ASP
Request.ServerVariables(“HTTP_X_FORWARDED_FOR”)
PHP
$_SERVER[“HTTP_X_FORWARDED_FOR”]
JSP
request.getHeader(“HTTP_X_FORWARDED_FOR”)

似乎是要我修改我的网站的代码吧,不过这指标不治本,况且Ioncube加密后的,修改是没什么可能的了,问他们能否修改Nginx配置文件实现,不理我了……

看来还是得靠自己。他们既然能把那些代码发来,就证明他们的cdn服务器把真实IP存放在了HTTP_X_FORWARD_FOR里面,那么让nginx处理一下CDN服务器发来的xff的内容就行了了。

首先,取消Varnish处理XFF,把CDN传输的XFF直接交给Nginx处理,也就是注释掉以下内容:

然后在nginx的对应域名的setver层的location /层加进一行代码:

重启Nginx,打开探针看了看,终于正常了……

PHP探针