r5 - 17 Jun 2008 - 08:08:17 - Main.yfangYou are here: YWiki >  MyTech Web > 让你的Linux网关更强大(防火墙)

让你的Linux网关更强大(防火墙)

前言

  • 这里将简单介绍一下yfang本人搭建Linux网关和逐步加强网关功能的一些经验,可能对于IT管理员有一定借鉴意义。
  • 为了说明的更清楚一些,我将整个文档分成了几个部分,针对个人对系统和软件不同了解情况,各位可以跳过一些步骤。
  • written by yfang(yangfang@fudan.edu.cn)
  • 欢迎转载,转载请注明出处及作者信息

Linux防火墙发展简史

  • 说明一下,这里我们只谈Linux防火墙,也就是说,硬件防火墙和其他OS下的防火墙我们不涉及到。

  • 是Linux操作系统作为一个类Unix网络操作系统,在系统的稳定性、健壮性及价格的低廉性方面独具优势。基于Linux的防火墙软件不但具有强大的功能,而且大部分都是开放软件,随着Internet的飞速发展,安全问题越来越重要。利用Linux构建企业网深受中小企业的青睐,而利用Linux构建企业网的防火墙系统也成为众多中小企业的理想选择。
  • Linux内核从1.1版本开始,就已经具备包过滤功能。在2.0内核中,开始采用Ipfwadm来操作内核的包过滤规则。到2.2版本时,Linux内核采用了Ipchains来控制内核的包过滤规则。发展到2.4.x时,Ipchains被一个全新的包过滤管理工具Iptables所替代。2.6版内核也在安全方面进行了改进。因此,无论拥有哪个版本的Linux内核,无论选择哪个版本的Linux来构建自己的企业网,都可以利用现有的系统构建出一个理想实用的防火墙。
  • 防火墙系统可分为包过滤型、应用级网关(也叫代理服务器型防火墙)和电路级网关三种基本类型。Linux提供的防火墙软件包内置于Linux内核中,是一种基于包过滤型的防火墙实现技术。其中心思想是根据网络层IP包头中的源地址、目的地址及包类型等信息来控制包的流向。更彻底的过滤则是检查包中的源端口、目的端口以及连接状态等信息。

Iptables的基本介绍

  • Iptables,严格意义上说应该说是netfilter/iptables,是从2.4.x 版本Linux内核开始内核级集成的IP信息包过滤系统。到目前(2008年)为止依然是最好的Linux下的防火墙。这里借用iptables v1.3.5的manaul中的介绍,administration tool for IPv4 packet filtering and NAT。明确地说,两个功能,一个是包过滤,一个是NAT转发(之所以这里只提到ipv4,是因为ipv6的相关功能在另外一个叫做iptables-ipv6的包里,这里就不做过多介绍了)。而这两个功能恰好是典型的防火墙和网关做的事情。
  • 关于iptables的语法和原理,本文中不会介绍,如果有兴趣,欢迎访问我的另一篇文章Iptables基础学习

1. 实战准备

  • 在真正开始实战前,让我们明确一下系统环境和目的
  • 系统环境
型号 Dell Poweredge SC1425
内存 2G
CPU Intel(R) Xeon(TM) CPU 2.80GHz x2
eth0 Intel Corporation 82541GI/PI Gigabit Ethernet Controller
eth1 Intel Corporation 82541GI/PI Gigabit Ethernet Controller

  • 软件环境
操作系统 CentOS4.6
Kernel 2.6.9-67.0.7.ELsmp
Iptables iptables-1.2.11-3.1.RHEL4

  • 目标 :使用iptables搭建一个基本的应用级网关防火墙,实现外网和内网的简单通信,实现一个简单的端口映射和ip/mac绑定的网关

防火墙过滤策略的选择——永远不够安全

  • 一般防火墙的过滤策略有两个常用思路:允许任何服务除非被明确禁止(Allow-all Strategy);禁止任何服务除非被明确允许(Deny-all Strategy)。显然前一种策略易用而相对危险,后一种难用而相对安全。对于后一种而言,网络管理员需要清楚的了解提供的端口和协议,以保证正常服务的运转。企业级防火墙很多都只提供后面一种策略。
  • 对于iptables来说,对应的实现方式就是所谓的默认规则,通常使用-P参数在iptables的规则里设定
#       以下内容摘自iptables manpage
#       iptables [-t table] -P chain target [options]

#       -P, --policy chain target
#              Set the policy for the chain to the given target.  See the section TARGETS for the  legal  targets.
#              Only  built-in  (non-user-defined)  chains can have policies, and neither built-in nor user-defined
#              chains can be policy targets.

#       下面的代码摘自前面一节构建基础网关,设定默认允许所有链上的默认规则为允许
#       实际上这是一种Allow-all策略,后面我们会解释为什么选用这种策略

IPTABLES=/sbin/iptables

$IPTABLES -P INPUT ACCEPT
$IPTABLES -P FORWARD ACCEPT
$IPTABLES -P OUTPUT ACCEPT

$IPTABLES -t nat -P PREROUTING ACCEPT
$IPTABLES -t nat -P POSTROUTING ACCEPT
$IPTABLES -t nat -P OUTPUT ACCEPT

#       对一台普通的服务器而言,以上的ACCEPT均可以简单的换成DROP,以实现Deny-all策略
#       这里不得不多说一句,我的前提是你很熟悉iptables机制或者这台服务器就在你的手边,可以直接本机登陆
#       否则假如你把规则指定成Deny-all,有没有开启对自己的允许,然后远程登录不上去了,可不要找我麻烦 :-)

  • 在我不停的鼓吹Deny-all策略的时候,你可能已经看到了,我的网关却设定了默认的ACCEPT规则,这不是很奇怪的事情么!事实上一点也不。让我们再明确一下此次实验的目标,没错,一个基本的应用级网关防火墙。首先,它是一个网关,网关就要用NAT,我们再来回忆一下多年前网络课程上老师对NAT实现机制的说明吧,下面是第四章讲解NAT的PPT中的一页
    nat-sample.jpg
  • 很明显,网关上维护的NAT translation table列出了网关至少要开哪些对外端口,通常对于一个上百台同时访问外网的局域网来说,这张表通常有成千上万条记录,规模再大一些,再碰上一帮用p2p软件的哥们儿,这个值就可能逼近ip_conntrack_max值(这个最大65536)了。
# 执行下面的命令可以查看你的网关中NAT表情况
cat /proc/net/ip_conntrack

# 下面的命令帮你明确一下NAT表的规模
wc -l /proc/net/ip_conntrack
#或者
grep ip_conntrack /proc/slabinfo | awk '{print $1 ',' $2;}'

# 下面的命令帮你统计NAT表中占用端口最多的几个ip,很有可能这些家伙再做一些bt的事情,嗯bt的事情:-)
cat /proc/net/ip_conntrack | cut -d ' ' -f 10 | cut -d '=' -f 2 | sort | uniq -c | sort -nr | head -n 10
tip想要了解更多的NAT相关的Linux系统知识,参见Linux系统上的NAT
  • 这么多个nat在,我们几乎不可能在Deny-all策略下针对端口或协议开放我们需要的服务(对于那种非网管服务器,这种情况就完全不同了)。退一万步说,即便我们设定了默认Allow all,依然可以在设定规则的最后一条加上类似与all drop的规则,虽然这样看上去很傻。后面ip和mac绑定的实现中就有类似的实现。不管怎么说,我们总是想尽办法让我们的服务器尽量少的allow,通常可以安全一些。

让你的网关轻松配置端口映射

  • 本文的内容前面的部分多少有点枯燥,下面让我们看点实在的东西吧。
  • 端口映射(Port Map)
    • 对于一个高级一点的网关来说,它不仅担负着本地对外访问时IP伪装和阻挡危险数据的防火墙的功能,它经常同时具备外部访问内部的任务分发功能。这种任务分发使得外部客户端直接访问内部服务成为可能,同时也避免了把网关作为一个真实服务提供者的身份带来的危险,比如外界对某服务的攻击可能带来网关的崩溃。
  • 几乎所有路由器都支持端口映射的配置,我们要做一个功能完善一点的网关,最好还是支持一下这个重要的功能。而且事实上,Linux的内核和强大的iptables可以帮助你轻而易举的完成这件事情。下面我来说明一下最简单的一个端口映射应该怎么做(引自Linux系统上的NAT
# 实现网关的简单端口映射
# 具体功能:实现外网通过访问网关的外部ip:80,可以直接达到访问私有网络内的一台主机192.168.1.10:80效果

LOCAL_EX_IP=11.22.33.44 #设定网关的外网卡ip,对于多ip情况,参考《如何让你的Linux网关更强大》系列文章
LOCAL_IN_IP=192.168.1.1  #设定网关的内网卡ip
INTERNAL="eth1" #设定内网卡

# 这一步开启ip转发支持,这是NAT实现的前提
echo 1 > /proc/sys/net/ipv4/ip_forward

# 加载需要的ip模块,下面两个是ftp相关的模块,如果有其他特殊需求,也需要加进来
modprobe ip_conntrack_ftp
modprobe ip_nat_ftp

# 这一步实现目标地址指向网关外部ip:80的访问都吧目标地址改成192.168.1.10:80
iptables -t nat -A PREROUTING -d $LOCAL_EX_IP -p tcp --dport 80 -j DNAT --to 192.168.1.10

# 这一步实现把目标地址指向192.168.1.10:80的数据包的源地址改成网关自己的本地ip,这里是192.168.1.1
iptables -t nat -A POSTROUTING -d 192.168.1.10 -p tcp --dport 80 -j SNAT --to $LOCAL_IN_IP

# 在FORWARD链上添加到192.168.1.10:80的允许,否则不能实现转发
iptables -A FORWARD -o $INTERNAL -d 192.168.1.10 -p tcp --dport 80 -j ACCEPT

# 通过上面重要的三句话之后,实现的效果是,通过网关的外网ip:80访问,全部转发到内网的192.168.1.10:80端口,实现典型的端口映射
# 特别注意,所有被转发过的数据都是源地址是网关内网ip的数据包,所以192.168.1.10上看到的所有访问都好像是网关发过来的一样,而看不到外部ip
# 一个重要的思想:数据包根据“从哪里来,回哪里去”的策略来走,所以不必担心回头数据的问题

# 现在还有一个问题,网关自己访问自己的外网ip:80,是不会被NAT到192.168.1.10的,这不是一个严重的问题,但让人很不爽,解决的方法如下:
iptables -t nat -A OUTPUT -d $LOCAL_EX_IP -p tcp --dport 80 -j DNAT --to 192.168.1.10
  • 这是最简单的情况,为了实施方便,这里提供一个我写的ezportmap.sh,原理和上面的脚本类似,功能方面有以下优点:
    1. 支持外网多ip情况
    2. 支持外网ip限制
    3. 直接写配置,不需要读懂代码
    4. 即插即用,脚本中在FORWARD链中插入一条自己创建的CFORWARD链,所有实际功能都写在CFORWARD链中,如果想要取消端口映射功能,只需把这条链从FORWARD链中移出。(当然,这里可能还要麻烦你把PREROUTING,POSTROUTING,OUTPUT中的相关规则也处理一下才干净,我的意思只不过是移出CFORWARD足以禁用外网的端口映射而已)
#!/bin/sh

#=======================================
#===Author  : yang.fang              ===
#===Email   : yangfang@fudan.edu.cn  ===
#===MSN     : yang.fang@adways.net   ===
#===QQ      : 78461151               ===
#=======================================

# Easy port mapping shell script
# Created by yangfang 2006.8.21
# Last modified by yangfang (2008.3.31)

# Change log
# 2006.8.21 yang.fang beta version. can do.
# 2007.5.11 yang.fang 0.1 version. use array to store LOCAL_EX_IPs.
# 2008.3.31 yang.fang 0.2 version. fix firewall itself can not reach port map server bug.
# 2008.3.31 yang.fang 0.3 version. cut the ALL type down, for it does no use. enable config check.

# base setting

IPTABLES=/sbin/iptables
IPTABLESSAVE=/sbin/iptables-save
IPTABLESRESTORE=/sbin/iptables-restore
EXTERNAL="eth0"
INTERNAL="eth1"

# Set IP for local host, iptalbes may need it
# Without configuring ips, use the setting of devices $EXTERNAL and $INTERNAL in system

LOCAL_EX_IP=('11.12.13.14' '11.12.13.15'); # Here list all your external ip candidates
#LOCAL_IN_IP=192.168.0.1 # 

# Set your forward chain name, CFORWARD as default
# You may also define it as iptables' FORWARD, but it is dangers for unexpect change.
# CHAIN_NAME='CFORWARD'

# standard format of configuration
# MAPPING_TYPE LOCAL_PORT DEST_IP:DEST_PORT
# Be care of port conflict.

# the 4th domain of config is access limit rule
# only ips in '[]' are permited to use the port mapping
# use '#' to part each ips, DO NOT use any space in the ip limit part
# types like 12.34.56.78 and 12.34.56.78/16 are supported

# You may add your portmapping table beloww:
MAP_RULE=(
"tcp 21 192.168.0.2:21"
"tcp 22 192.168.0.3:22"
"tcp 80 192.168.0.13:8080"
"tcp 443 192.168.1.3:443 [12.34.56.0/24#58.156.232.144/28#192.168.0.0/24]"
"tcp 1001 192.168.1.2:3306 [12.34.56.78]"
);

# set ips if not configured before
if [[ $LOCAL_EX_IP = '' ]]
then
    LOCAL_EX_IP=($(/sbin/ifconfig $EXTERNAL|grep inet\ addr:|cut -d':' -f2|cut -d' ' -f1))
fi
if [[ $LOCAL_IN_IP = '' ]]
then
    LOCAL_IN_IP=$(/sbin/ifconfig $INTERNAL|grep inet\ addr:|cut -d':' -f2|cut -d' ' -f1)
fi
if [[ $CHAIN_NAME = '' ]]
then
    CHAIN_NAME='CFORWARD'
fi

# check configuration
MAX_LINE=$(for((index=0; index<${#MAP_RULE[@]}; index++)) 
    do
        echo ${MAP_RULE[index]}|cut -d' ' -f2;
    done | sort | uniq -c|sort|tail -1)
MAX_NUM=$(echo $MAX_LINE|awk '{print $1;}');

if [ $MAX_NUM -gt 1 ]
then
   echo "[Error] Duplicat port found : $(echo $MAX_LINE|awk '{print $2;}')";
   echo "Please check your configuration";
   exit 1;
fi
    
# modprobe ftp modules
modprobe ip_conntrack_ftp
modprobe ip_nat_ftp

# flush existed nat rules
$IPTABLES -t nat -P PREROUTING ACCEPT
$IPTABLES -t nat -P POSTROUTING ACCEPT
$IPTABLES -t nat -P OUTPUT ACCEPT
$IPTABLES -t nat -F OUTPUT
$IPTABLES -t nat -F PREROUTING
#$IPTABLES -t nat -X
($IPTABLES -N $CHAIN_NAME && $IPTABLES -A FORWARD -j $CHAIN_NAME) 2>/dev/null
$IPTABLES -F $CHAIN_NAME
${IPTABLESSAVE}|grep -v SNAT|${IPTABLESRESTORE}

# Execute portmap rule
for((index=0; index<${#MAP_RULE[@]}; index++)){
    rule=${MAP_RULE[index]};
    protocol=$(echo $rule|cut -d' ' -f1);
    out_port=$(echo $rule|cut -d' ' -f2);
    map_address=$(echo $rule|cut -d' ' -f3);
    map_server=$(echo $map_address|cut -d':' -f1);
    map_port=$(echo $map_address|cut -d':' -f2);
    allow_access=$(echo $rule|cut -d' ' -f4|cut -d'[' -f2|cut -d']' -f1);

    if [[ $allow_access != ''  ]]
    then
        allow_ip=(`echo $allow_access|tr '#' '\n'`);
    else
        allow_ip=();
    fi

    (for((ex_index=0; ex_index<${#LOCAL_EX_IP[@]}; ex_index++)){
        $IPTABLES -t nat -A PREROUTING -d ${LOCAL_EX_IP[ex_index]} -p $protocol --dport $out_port -j DNAT --to $map_address
        $IPTABLES -t nat -A OUTPUT -d ${LOCAL_EX_IP[ex_index]} -p $protocol --dport $out_port -j DNAT --to $map_address
        # Add the upper command so that the firewall itself can also reach the port map server.
    }  && \
    $IPTABLES -t nat -A POSTROUTING -d $map_server -p $protocol --dport $map_port -j SNAT --to $LOCAL_IN_IP && \
    if [[ ${#allow_ip[@]} -gt 0 ]]
    then
        for((ip_index=0; ip_index<${#allow_ip[@]}; ip_index++)){
            $IPTABLES -A $CHAIN_NAME -p $protocol -s ${allow_ip[ip_index]} -d $map_server --dport $map_port -j ACCEPT
        }
        $IPTABLES -A $CHAIN_NAME -p $protocol -d $map_server --dport $map_port -j DROP
    else
        $IPTABLES -A $CHAIN_NAME -p $protocol -d $map_server --dport $out_port -j ACCEPT
    fi ) &> /dev/null

    if [ "$?" -ne 0 ]
    then
        echo "Unknown mapping rule: \"$rule\"";
        echo "Break down for pity!"
        exit 1;
    fi
}
echo Setting port map done!
exit 0;

让你的网关轻松绑定ip和mac

参考资料

Topic attachments
I Attachment Action Size Date Who Comment
jpgjpg nat-sample.jpg manage 87.1 K 09 May 2008 - 02:41 Main.yfang NAT Sample
Edit | WYSIWYG | Attach | Printable | Raw View | Backlinks: Web, All Webs | History: r5 < r4 < r3 < r2 < r1 | More topic actions
 
Powered by YWiki
This site is powered by the TWiki collaboration platformCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding YWiki? Send feedback