Netfilter/iptables与NAT配置

通过iptables进行基础防火墙与各种NAT的配置

这次面临的问题是:希望把对主机A的访问(不局限在某一端口上)以类似代理的方式转嫁到B上;AB都有公网的IP,而不是内网的关系。

经过连夜奋战,实现了这一功能,自己也学到了一些经验。 

这里记录了基本所有的配置,顺带着把Netfilter/iptables也介绍了,希望对读者有帮助。 

本文系笔者原创文章,一天前以 aptitude 帐号发布于北邮真情流露 BBS。 

一、概述

NetFilter 是 Linux 2.4 kernel 开始引入的一套核心态-用户态(kernel space/user space) 配合的灵活的包过滤防火墙/高级路由器支持iptables 就是一个用户态工具,可以用来修改 Netfilter 规则,两者配合,可以在用户态来修改核心高级路由功能,不仅无须重新编译内核,也完全不必重新启动系统,即时修改、即时生效,所以,是 2.4 内核的标志性特性之一,远胜于 2.2 的 ipchains。

作为包过滤防火墙 Netfilter 可以根据一系列条件放行或是丢弃这些包,或是放行到送到用户空间去处理;作为高级路由器,Netfilter 可以根据 IP 包的某些特征对 IP 包头数据进行修改 (主要是源/目的地址/端口),从而达到 IP 伪装、端口映射、负载均衡等功能,而这次我们的工作主要就是利用了这些功能。

Netfilter 的另一大优点就是极具可扩展性,可以通过编写内核模块增加新的匹配、处理功能,其内部本身支持了两张表 (filter/nat),用于上述两类功能,你也可以增加新的功能;而每张表又会有不同的链 (chains),对应于不同的处理阶段;一个包从进入到出去可能会经历若干个表的若干个链,每个链会包含若干规则,一一对进入链的包进行处理,下面就听我细细讲来 (没兴趣操作的话了解到这里就差不多了。):

二、iptables 使用

2.1 表和链

首先,iptables 命令操作比需要指定表,如果没有指定,就会缺省认定是 filter 表,比如
    iptables -L -v
就会列出 filter 表中 INPUT/FORWARD/OUTPUT 三个链的规则,而要列出 nat 表的 PREROUTING/POSTROUTING/OUTPUT 就要用
    iptables -t nat -L -v

2.2 链的初始化

之后,在操作之前,可能会清除(flush)一个链上的所有规则,然后从零做起:
    iptables -t nat -F PREROUTING
当然,如果之前的规则不能删除就不要这样了。

每个链会有一个缺省的策略,比如,INPUT 链是用来处理所有目的为本机的刚刚进入的包的,对于这个链,我们常常设置为缺省丢弃 (DROP),然后特殊的让一些包可以通过,这样比较安全一些:
    iptables -P INPUT DROP

2.3 规则、INPUT 链

当然,这之后要开规则来允许某些包进来,比如,允许端口 23 的 tcp 访问:
    iptables -A INPUT -p tcp --dport 23 -j ACCEPT
-A 参数是在某个链加规则,之后是规则的两个部分: 条件和执行的操作。首先是匹配条件:

  •   -p tcp 是协议 tcp
  •   --dport 23 是目的(destination) 端口 23

常见的匹配条件还有 -s (source ip), -d (destination ip) 等,详细的可以 man 一下,这次配置,我们还用了另一个匹配方式:

     -m state --state ESTABLISHED

-m 参数用于加载一类非内建的匹配方式: 连接状态匹配,STABLISHED 用于对于建立了socket 连接的包进行匹配,常常用于这样的情况: 禁止从外到内的连接,但对于从内到外建立的 socket 连接,其进入的包可以允许通过。

然后是操作,也就是目标 (TARGET)

  • -j ACCEPT 是跳 (jump) 到目标 ACCEPT 上去,常用于 INPUT 的另一个目标是DROP,也就是丢包,对于不喜欢的包都可以丢掉

2.4 DNAT/端口映射

所谓 NAT 是网络地址变换(翻译?),也就是在路由的同时修改源或目的地址/端口,来完成某种高级路由功能。比如,从 192.168.1.1/24 的网络出去,源地址是不合法的公网 IP 需要做一个调整,这个叫 SNAT,一般用网关的出口地址作为修改后的地址,这种情形更确切地被称为 IP 伪装 (MASQUERADE)。而另一种情况是吧访问到一台计算机的包重新送到另一台计算机/端口去,这个常常称为端口映射,规范地说,叫 DNAT,D 是
destination 的意思。

DNAT 一般在包进入之后、被决定路由之前来进行,所以发生在 PREROUTING 链:
    iptables -t nat -A PREROUTING -p tcp --dport 23 \
        -d dnat.server.ip.addr \
        -j DNAT --to bupt.org.ip.addr:23
如此,就把到公网主机的 telnet 访问的目的地址改变到了我们的 server 了。

2.5 SNAT/IP伪装

本来我以为上面那样就可以了,不过事实证明不太成功,研究后认为具体原因是,到达真情的包的源地址是访问者的地址,所以发回去的 ip 包不会经过我们的 DNAT server, 到访客的包的源地址也不是他请求的目的地址,这个自然地无法成功建立 TCP 连接。

所以,挠头半小时后,我决定加上 SNAT,也是在中间的 server
    iptables -t nat -A POSTROUTING -p tcp --dport 23 \
        -d bupt.server.ip.addr \
        -o eth0 -j MASQUERADE
这里有两点需要注意:

  • a) 目的地址的匹配应该是最终地址和端口,因为前面地址 DNAT 已经变过了。
  • b) IP 伪装一定要指定出端口 (-o interface) 否则鬼知道伪装成什么 IP,没准是 127.0.0.1 呢,呵呵。

2.6 允许数据包转发

    echo 1 > /proc/sys/net/ipv4/ip_forward

如果不允许转发 ip 包,所有配置都没用了,包不会被路由的,呵呵。

2.7 规则的删除:

    iptables -D INPUT ...

这个后面的写法完全和 iptables -A 相一致,必须一字不差才能删除

三、永久化我们的配置

iptables 配置如果没经过处理的话,在每次重启后都要失效,所以应该永久化一下,昨天晕,忘了这个,不过暇不掩瑜,总算是基本配出来了,最后这个 ever 加一下吧。

如果是 rh ,里面已经提供了一项 iptables 服务了,运行
    service iptables save
应该差不多就行了,不过我给兄弟们一个万能的方法:

  • 1 在 /etc/init.d/ 之下建立一个脚本,加上执行权限
  • 2 在脚本上写上我给出的那些东东:
      用那句 echo 打开 ip_forward
      INPUT 上加三条规则(telnet,www 和那个 state 的)
      PREROUTING 上加两条规则 (telnet,www)
      POSTROUTING 也两条
  • 3 在 /etc/rc[2-5].d/ 里面都加上到这个脚本的符号链接,如 S20buptnat.sh ->../init.d/buptnat.sh (S20应该是可以的,理论上讲,过了 rcS.d 的 S40,网络功能就应该已经具备了,这是对 Debian 而言的,其它类似)

四、总结

好长,写完了,呵呵,都是命令的使用,难登大雅之堂,对大家有什么参考价值的话就再好不过了 :)