背景:我们的产品包含即时通讯、社区等功能。现在我们需要简单地测量一下产品的网页流量、推送流量等信息,这些信息可以帮助我们更加合理地选用VPS的带宽和流量。虽然有许多工具包含了这个功能,但我考虑到我们只需要这一个功能,而同类工具大多都比较重型,并且有的可能还不是很好部署,因此直接使用iptables统计的流量信息。
iptables -L -n -v可以对每一条匹配上的规则,显示此规则所匹配到的流量。由于是内核级别的统计,因此在准确度上是足够的。由于我们需要统计的流量非常模式化(只有特定的三个端口的上行和下行),因此iptables完全可以胜任这种需求。流量会以人类可读的格式显示。每一行即为一条规则的流量。由于iptables的特性,只要符合一条规则就不会继续处理下去,因此这个列表不会重复计算同一份流量。
我们的服务器使用80、443、1813端口。在流量监测中,我们发现这两个端口的入流量很小,这不符合我们的直觉。观察iptables规则发现,其中有一条规则:
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
这条规则会匹配状态为已连接和已释放的包。由于这条规则十分靠前,因此只要连接建立后,数据包的流量都经过了这条规则,而没有如预计般,按照端口分类统计。我们知道,iptables的匹配链如果过长,那么性能会受一定程度的影响。如果在最前端加入了这样的规则,那么只要第一次建立连接时成功,那么后续的数据包都可以立即通过,无需再重新走规则链。这样就能够有效缓解因匹配链过长而带来的性能损失。
于是问题来了。我粗心地将这条规则注释掉,并重新加载iptables规则。然后在这一瞬间,ssh会话就hung住不动了。我立即知道iptables规则出了错,错误地将ssh的数据包阻止了。但我没想明白的是背后的原理,我明明没有改动ssh的规则。而且,业务端口仍然可以正常访问。
ssh无法连通,我只好寄希望与阿里云的web控制台。然后不巧的是,web控制台的VNC密码是默认的随机密码,而那个随机密码已经没人记得了。
阿里云提供了重置vnc密码的功能,但它需要重启服务器实例。由于是生产服务器,因此不能随意重启。我们终于等到了服务器相对空闲的时候,重启了服务器,顺利地进入了VNC会话。
然后我们发现,我们没有登陆密码。平时我们使用ssh证书登陆为一个非root用户,这个用户的密码大家都不记得,而root用户的密码也是随机的。
幸好阿里云又提供了重置root用户密码的功能。于是我们终于能够以root用户进入服务器,然后解除iptables的错误限制,ssh终于可以登陆了。
检查iptables规则,发现ssh的规则里,不知是有意还是无意,比其他的规则还多限定了连接状态:
-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT
于是这条规则,实际上只对新连接有效,而后续的数据包正是通过被我注释掉的那一句被放行的。服务所需的那几个端口没有加这样的限制,于是web服务可以继续运行下去,没有干扰用户的使用。
反思:
1、iptables强大但危险。改动规则之前仔细查看现有的规则,防止破坏规则链。
2、至少保证web控制台可用。一旦出了问题无法ssh(比如流量过大、ssh进程死掉等极端情况下),可以使用vnc控制机器。
3、平时不使用root用户,但至少让root用户能在tty下登陆。否则万一误操作删了数据之类的,又没法重置root用户密码的话,真的就麻烦了。