最近在自己的电脑上全新安装了 CentOS 6.6 虚拟机,随后顺手部署了 Nginx 服务,结果发现只有虚拟机内部可以正常访问,外部网络无论如何都无法连接。不用多想,第一反应就是防火墙设置出了问题。赶紧检查了一下 iptables 规则,发现当前的配置如下:
[root@centos6 ~]# iptables-sa ve # Generated by iptables-sa ve v1.4.7 on Sun Jul 26 15:53:13 2015 *filter :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [5819:366868] -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 22 -j ACCEPT -A INPUT -j REJECT --reject-with icmp-host-prohibited -A FORWARD -j REJECT --reject-with icmp-host-prohibited COMMIT # Completed on Sun Jul 26 15:53:13 2015
乍看上去,默认策略全都是 ACCEPT,似乎没什么问题。但再往下翻,最后两条规则很值得留意:
-A INPUT -j REJECT --reject-with icmp-host-prohibited -A FORWARD -j REJECT --reject-with icmp-host-prohibited
这两条规则的含义其实非常明确:在 INPUT 和 FORWARD 链中,凡是未被前面规则明确放行的数据包,统统会被拒绝,并向源主机返回一条“host prohibited”的 ICMP 消息。换句话说,整个防火墙只放通了三种流量:已建立连接的相关数据包、ICMP(ping)以及本机回环接口上的通信,外加一个 SSH(TCP 22 端口)。至于 Nginx 默认监听的 80 端口?很遗憾,被最后那条 REJECT 规则一棒子全部拦截了。
因此问题的根源就在这里:尽管默认策略是 ACCEPT,但规则链末尾有一条全量 REJECT 兜底,相当于把所有未明确允许的端口都封锁了。解决办法其实很简单,要么添加一条放行 80 端口的规则,要么直接将那条 REJECT 规则注释掉(前提是你清楚这样做的后果)。不过在生产环境中,更规范的做法是显式开放所需的端口,而不是直接关闭防火墙。说到底,这个问题的源头要归咎于 CentOS 6 默认的 iptables 模板——它只预留了 SSH 端口,其他服务一律禁止访问。
