为什么iptables不阻止IP地址? (LB /代理版本)

警告:长。 很多信息在这里。

3年前有人问为什么iptables不能阻塞IP地址? 原因是因为服务器在CloudFlare的后面,这使得不可能按照他们想要的方式直接阻止IP地址,除非您以不同的方式使用它。 任何反向代理或负载平衡器都会导致同样的情况。

同样,我们已经build立了一个规则fail2ban来禁止任何试图暴力进入pipe理login或垃圾邮件xmlrpc的僵尸程序。 站点坐在负载平衡器后面,所以显然我们不能直接禁止IP地址,但是iptables应该接受与分组数据匹配的连接和模式以禁止特定的stream量。

这是fail2ban jail.confconfiguration:

[wp-auth] enabled = true filter = wp-auth action = iptables-proxy[name = lb, port = http, protocol = tcp] sendmail-whois[name=LoginDetect, [email protected], [email protected], sendername="Fail2Ban"] logpath = /obfuscated/path/to/site/transfer_log bantime = 604800 maxretry = 4 findtime = 120 

这是wp-login请求的简单模式匹配:

 [Definition] failregex = ^<HOST> .* "POST /wp-login.php ignoreip = # our ip address 

这是我们的fail2ban iptables行动,应该能够阻止这些机器人,但大部分似乎并不。 它来自于代理服务器后面的fail2ban的CentOS站点提示部分。 为了简洁起见,我仅留下了部分标题注释。

 # Fail2Ban configuration file # # Author: Centos.Tips # [INCLUDES] before = iptables-blocktype.conf [Definition] # Option: actionstart actionstart = iptables -N fail2ban-<name> iptables -A fail2ban-<name> -j RETURN iptables -I <chain> -p <protocol> --dport <port> -j fail2ban-<name> # Option: actionstop actionstop = iptables -D <chain> -p <protocol> --dport <port> -j fail2ban-<name> iptables -F fail2ban-<name> iptables -X fail2ban-<name> # Option: actioncheck actioncheck = iptables -n -L <chain> | grep -q 'fail2ban-<name>[ \t]' # Option: actionban actionban = iptables -I fail2ban-<name> 1 -p tcp --dport 80 -m string --algo bm --string 'X-Forwarded-For: <ip>' -j DROP # Option: actionunban actionunban = iptables -D fail2ban-<name> -p tcp --dport 80 -m string --algo bm --string 'X-Forwarded-For: <ip>' -j DROP [Init] # Default name of the chain name = default # Option: port port = http # Option: protocol protocol = tcp # Option: chain chain = INPUT 

所以正如我所提到的,这个站点位于弹性负载均衡器后面的一对服务器上,似乎在testing中工作。 我们可以添加任何我们自己的IP地址,我们无法到达该网站。 尽pipe这个机器人似乎能够通过。

 [root:~/] iptables -S -P INPUT ACCEPT -P FORWARD ACCEPT -P OUTPUT ACCEPT -N fail2ban-SSH -N fail2ban-lb -A INPUT -p tcp -m tcp --dport 80 -j fail2ban-lb -A INPUT -p tcp -m tcp --dport 22 -j fail2ban-SSH -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 5666 -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 3306 -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 24007:24020 -j ACCEPT -A INPUT -j REJECT --reject-with icmp-host-prohibited -A FORWARD -j REJECT --reject-with icmp-host-prohibited -A fail2ban-SSH -j RETURN -A fail2ban-lb -p tcp -m tcp --dport 80 -m string --string "X-Forwarded-For: 91.200.12.33" --algo bm --to 65535 -j DROP -A fail2ban-lb -p tcp -m tcp --dport 80 -m string --string "X-Forwarded-For: 91.134.50.10" --algo bm --to 65535 -j DROP -A fail2ban-lb -p tcp -m tcp --dport 80 -m string --string "X-Forwarded-For: 160.202.163.125" --algo bm --to 65535 -j DROP -A fail2ban-lb -p tcp -m tcp --dport 80 -m string --string "X-Forwarded-For: 162.243.68.232" --algo bm --to 65535 -j DROP -A fail2ban-lb -j RETURN 

港口80是唯一向所有人开放的港口。 所有其他人都通过AWS安全组进行ACL。 IPtables似乎按照正确的顺序处理,因此应该根据它们的X-Forwarded-For头来阻止这些IP。 有一个Firefox插件,它允许你发送这些头部的初始请求,我们也因此被阻止。

来源IP地址似乎并没有伪造X-Forwarded-For头部,正如我们一直在玩ELB重写它们一样。 tcpdump在服务器级别上不显示有关数据包的任何额外信息。

 22:07:14.309998 IP ip-10-198-178-233.ec2.internal.11054 > ec2-10.4.8.71.http: Flags [P.], seq 2545:3054, ack 19506, win 166, options [nop,nop,TS val 592575835 ecr 2772410449], length 509 E..1..@[email protected] ... f.p+..P.Nz. 20............ #Q.[.?.QPOST /wp-login.php HTTP/1.1 host: www.thiswebsite.com Accept: */* Accept-Language: zh-cn Cache-Control: no-cache Content-Type: application/x-www-form-urlencoded Referer: http://www.thiswebsite.com/wp-login.php User-Agent: Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1; 125LA; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022) X-Forwarded-For: 91.200.12.33 X-Forwarded-Port: 80 X-Forwarded-Proto: http Content-Length: 21 Connection: keep-alive 

这些请求都被logging在transfer_log中。 当我们做同样的事情,伪造X-Forwarded-For我们在到达Apache之前被iptables抓住了。 tcpdump也显示我们的额外的IP地址。

 20:10:25.378873 IP ip-10-198-178-233.ec2.internal.11054 > ec2-10.4.8.71.http: Flags [P.], seq 3157:3860, ack 124583, win 267, options [nop,nop,TS val 526293643 ecr 2507283790], length 703 E...Tf@.@.[. ... fp,OP..GU........m..... .^...r.QPOST /wp-login.php HTTP/1.1 host: www.thiswebsite.com Accept: / Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.5 Cache-Control: no-cache Cookie: __utma=190528439.16251225.1476378792.1478280188.1478289736.3; __utmz=190528439.1476378792.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _icl_current_language=en; __utmc=190528439; __utmb=190528439.2.10.1478289736; __utmt=1 Pragma: no-cache Referer: http://www.thiswebsite.com/ User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:49.0) Gecko/20100101 Firefox/49.0 X-Forwarded-For: 91.200.12.33, <our ip address> X-Forwarded-Port: 80 X-Forwarded-Proto: http Connection: keep-alive 

我也有ELB访问日志,我希望看到一个条目,而不是Apache传输日志。

 2016-11-07T22:07:14.309917Z mLB 91.200.12.33:60407 10.4.8.71:80 0.000079 1.99244 0.000091 200 200 21 3245 "POST http://www.thiswebsite.com:80/wp-login.php HTTP/1.1" "Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1; 125LA; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)" - - 

所以IP地址(至less根据ELB)在X-Forwarded-For级别似乎不被强制。 为什么从它的stream量不被阻止? IP地址也会在fail2ban日志中持续显示

 fail2ban.actions[11535]: INFO [wp-auth] 91.200.12.33 already banned 

你的iptables规则看起来不错。 一个不能确定他们传递的是什么,但是,没有logging接受。 Tcpdump不会告诉你这个,因为它在iptables运行之前在传入时运行。 由于你需要一个负载平衡器,所以端口80的iptables-accept日志可能会产生非常大的文件,你需要仔细pipe理(用于磁盘使用),你需要脚本或其他工具来分析它们。 然而,这是你需要做的,以了解什么是通过。

但是,从上面我可以告诉你,使用networking包string匹配进行应用级过滤存在内在的泄漏问题。 数据包边界不尊重应用程序边界,因此在高度攻击的环境中,这些请求中的一部分将会泄露。 这是一个统计的效果。 如果有足够的请求指向您的系统,那么其中一些请求将被拆分为多个数据包的可能性会增加。 只有这一点可以解释泄漏。 黑客可以通过插入可以增加数据包大小的标题来改变他们的偏好。 但是单独的数量就够了。

为了彻底过滤,您需要在应用程序级别进行过滤。 Apache为此提供了几种机制。 您可以使用X-Forwarded-For的.htaccess规则来阻止由fail2ban标识的用户。 您也可以过滤您的位置指令。 我在fail2ban中看不到这样做的选项,也没有看到这样做的独立实用程序,但是将fail2ban jail-ees与apache筛选器同步的自定义脚本将是实现应用程序级筛选器的一种方法。

再考虑一下:你发布了IPv4的规则。 如果您在端口80上接受IPv6连接,则需要确保这些规则也得到维护。

这听起来像fail2ban实际上并没有制定你认为的新规则。 当你看到“已经被禁止”的消息,你做一个“iptables -L”,看到所需的规则? 一条“已经被禁止”的消息表明你的检测正在工作,但是你的防火墙不是。 如果你看到这个规则,但它不起作用,那就意味着有关规则的某些东西不起作用,你需要重新思考规则。

你的failregex和ignoreip告诉我你只是想阻止所有不是你的IP地址的人。 这听起来比使用fail2ban更简单。

事实上,您的pipe理控制台甚至需要负载平衡器吗? 如果你知道什么地址允许,而你的failregex和ignoreip指出你这样做,你可以直接访问pipe理控制台,但只允许访问允许的IP地址。

哦,嘿,这不是SSL吗? 如果是这样,iptables不能读取X-Forwarded-For头。

我非常喜欢HBruijn关于使用cloudflares的API的想法。 他们应该做的阻止,而不是你。

最后,如果你决定在apache级别进行过滤,mod_rewrite会重写这个地图 。 dbd和prg特性使您可以在不重新启动apache的情况下更改映射。

希望有一些帮助

-Dylan