重启 iptables 影响 nf_conntrack 参数说明

今天在新机房的机器上重启 iptables 后发现 net.nf_conntrack_max(最大跟踪的连接数) 会恢复成默认的 65536(RAM > 4G时, 该值默认为 65536), 在 stop iptables 后, 通过命令 sysctl -a |grep nf_conntrack 查看已经不存在 nf_conntrack 模块相关的参数信息. 从这点来看参数恢复成默认值本质上是因为在重启 iptables 的过程中重新加载了 nf_conntrack 模块;

在其它机房并未重现该问题, 查看 /etc/init.d/iptables 脚本, 跟踪到 stop 函数中的代码

IPTABLES=iptables
IPV=${IPTABLES%tables}  # ip
...
NF_MODULES=($(lsmod | awk "/^${IPV}table_/ {print \$1}") ${IPV}_tables)  # iptable_filter
NF_MODULES_COMMON=(x_tables nf_nat nf_conntrack) # Used by netfilter v4 and v6
...

stop() {
    ...
    if [ "x$IPTABLES_MODULES_UNLOAD" = "xyes" ]; then
        echo -n $"${IPTABLES}: Unloading modules: "
        ret=0
        for mod in ${NF_MODULES[*]}; do
            rmmod_r $mod
            let ret+=$?;
        done
        # try to unload remaining netfilter modules used by ipv4 and ipv6 
        # netfilter
        for mod in ${NF_MODULES_COMMON[*]}; do
            rmmod_r $mod >/dev/null
        done
        [ $ret -eq 0 ] && success || failure
        echo
    fi
    ...
}

脚本从 /etc/sysconfig/iptables-config 文件中读取配置 IPTABLES_MODULES_UNLOAD, 判断是否为 yes, 默认都是 yes, 则 stop 函数执行卸载模块操作, rmmod_r 函数如下:

rmmod_r() {
    ...
    # Get referring modules.
    # New modutils have another output format.
    [ $NEW_MODUTILS = 1 ] \
        && ref=$(lsmod | awk "/^${mod}/ { print \$4; }" | tr ',' ' ') \
        || ref=$(lsmod | grep ^${mod} | cut -d "[" -s -f 2 | cut -d "]" -s -f 1)

    # recursive call for all referring modules
    for i in $ref; do
        rmmod_r $i
        let ret+=$?;
    done

    # Unload module.
    # The extra test is for 2.6: The module might have autocleaned,
    # after all referring modules are unloaded.
    if grep -q "^${mod}" /proc/modules ; then
        modprobe -r $mod > /dev/null 2>&1
        res=$?
        [ $res -eq 0 ] || echo -n " $mod"
        let ret+=$res;
    fi
    ...
}

rmmod_r 函数通过递归方式卸载从 stop 函数传过来的模块名(注意 NF_MODULES_COMMON 信息); modprobe -r $mod 即为卸载模块操作, 该函数会卸载 stop 函数传过来的 nf_conntrack 模块; 在新机房中手工操作 modprobe -r nf_conntrack 可以正常卸载, 在其它机房手工操作,提示错误:

FATAL: Module nf_conntrack is in use.

跟踪模块信息:

[root@cz ~]# lsmod | grep '^nf_conntrack'
nf_conntrack_ipv6       7985  2 
nf_conntrack           79206  3 nf_conntrack_ipv4,nf_conntrack_ipv6,xt_state

可以看到 nf_conntrack 被三个模块使用, 对比新机房的信息:

[root@database6 ~]# lsmod | grep '^nf_conntrack'
nf_conntrack_ipv4       9154  1 
nf_conntrack           79206  2 nf_conntrack_ipv4,xt_state

可以看到 nf_conntrack_ipv6 模块的使用才是本文问题的原因, 由此可以关联到 ip6tables 正在使用 nf_conntrack 模块, 所以没有卸载 nf_conntrack 模块, 我们关掉 ip6tables 后, 看看其它机房的机器能否重现该文的问题:

[root@db02 ~]# /etc/init.d/ip6tables stop
ip6tables: Setting chains to policy ACCEPT: filter         [  OK  ]
ip6tables: Flushing firewall rules:                        [  OK  ]
ip6tables: Unloading modules:                              [  OK  ]
[root@db02 ~]# 
[root@db02 ~]# 
[root@db02 ~]# sysctl -a |grep nf_conntrack
[root@db02 ~]#
[root@db02 ~]# /etc/init.d/ip6tables start
ip6tables: Applying firewall rules:                        [  OK  ]
[root@cz ~]# sysctl -a |grep nf_conntrack_max
net.netfilter.nf_conntrack_max = 65536
net.nf_conntrack_max = 65536

以上重现了新机房的问题, conntrack 参数恢复成了默认值. 再来看看 ip6table 的设置:

[root@db02 ~]# ip6tables -vnL
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 ACCEPT     all      *      *       ::/0                 ::/0                state RELATED,ESTABLISHED 
    0     0 ACCEPT     icmpv6    *      *       ::/0                 ::/0                
    0     0 ACCEPT     all      lo     *       ::/0                 ::/0                
    0     0 ACCEPT     tcp      *      *       ::/0                 ::/0                state NEW tcp dpt:22 
    0     0 REJECT     all      *      *       ::/0                 ::/0                reject-with icmp6-adm-prohibited 

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 REJECT     all      *      *       ::/0                 ::/0                reject-with icmp6-adm-prohibited 

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

允许所有的 ipv6 地址可以连接 22 端口:

# ping6 -I em1 fe80::eef4:bbff:fecd:d224
PING fe80::eef4:bbff:fecd:d224(fe80::eef4:bbff:fecd:d224) from fe80::be30:5bff:feed:fde0 em1: 56 data bytes
64 bytes from fe80::eef4:bbff:fecd:d224: icmp_seq=1 ttl=64 time=2.10 ms
64 bytes from fe80::eef4:bbff:fecd:d224: icmp_seq=2 ttl=64 time=0.183 ms

# ssh -6 fe80::eef4:bbff:fecd:d224%em1
Last login: Tue Nov  3 14:04:05 2015 from 172.30.0.30
[root@cz ~]#  

从这点来看, ipv6 还是存在安全隐患, 不过很多运营商通常都没有配置 ipv6 相关的路由, 这里的通信仅限于内网的机器, 不过到底是多了一个安全隐患;

综上, 严格来看重启 iptables/ip6tables 会重新加载 nf_conntrack 相关的模块, 引起参数恢复成默认值, 在有应用的主机中需要引起重视. 当然为了机房之间机器的统一, 可以在新机房启用 ip6tables, 以免重启 iptables 引起参数失效. 也可以在 /etc/sysconfig/iptables-config 文件中开启选项 IPTABLES_SYSCTL_LOAD_LIST=".nf_conntrack", iptables 重启后会进行 sysctl 操作.