Linux 2.4 Packet Filtering HOWTO <author>作者:Rusty Russell, mailing list <tt>netfilter@lists.samba.org</tt> <newline>译者:网中人 <tt>netmanforever@yahoo.com</tt> <date>v1.0.1 Mon May 1 18:09:31 CST 2000 <abstract> 此文件描述如何在 2.4 Linux kernel 上使用 iptables 对不良封包进行过滤。 </abstract> <!-- Table of contents --> <toc> <!-- Begin the document --> <sect>Introduction<label id="intro"> <p> 各位看官,欢迎到此一读! <p> 这里,我假设您已经知道什麽是 IP 地址、网路地址、网路遮罩 (netmask)、路由、以及 DNS。否则,我建议您先读一读 Network Concepts HOWTO。 <p> 此 HOWTO 文件不止於是一个点到即止的介绍(让您有点发热和发毛,但又肉在砧上的感觉),也不至於是一个抽丝剥茧式的原始大披露(让您必有所获,但不会神经错乱、走火入魔)。 <p> 您的网路其实一点也不<bf>安全</bf>。问题难点是在允许快速而便利通讯之同时,又想确保它只用於良好且非邪恶的意图。其情形等同於在一个拥挤的戏院里面,允许您高谈阔论,但却不能乱喊“著火啦!”一样。这篇 HOWTO 不是用来解决这问题的。 <p> 所以,只有您才能决定何处才是折衷所在。我会尝试指引您去使用一些可用工具,也会点出要留意的死穴,当然,同时希望您用於正途之上。又是(与上句)同等的问题。 <sect>官方的网站在哪里?有邮件论坛吗? <p>有三个官方网站非去不可: <itemize> <item>感谢<url url="http://netfilter.filewatcher.org" name="Filewatcher (http://netfilter.filewatcher.org)">. <item>感谢 <url url="http://www.samba.org/netfilter" name="The Samba Team and SGI (http://www.samba.org/netfilter)">. <item>感谢<url url="http://netfilter.kernelnotes.org" name="Jim Pick (http://netfilter.kernelnotes.org)">. </itemize> <p>至於官方的 netfilter 邮件论坛,请参阅: <url url="http://lists.samba.org" name="Samba's Listserver (http://lists.samba.org)">. <sect>好了,什麽是封包过滤(Packer Filter)呢? <p> 封包过滤就是用一个软体查看所流经封包之<em>表头(header)</em> ,由此决定整个封包的命运。它或许会决定 <bf>丢弃(DROP)</bf> 这个封包(例如,忽略它就如根本没收到它一样),或是<bf>接收(ACCEPT)</bf>这个封包(例如,让这个封包通过),或是其它更复杂的动作。 <p> 在 Linux 之下,封包过滤功能是内建於核心之内(做为一个核心模组,或者直接内建),同时还有一些技巧我们可以运用於封包之上的,不过最惯用的依然是查看表头以决定封包的命运。 <sect1>我为什麽要封包过滤? <p> 简而言之:控制、保安、警戒。 <p> <descrip> <tag/Control:/当您用您的 Linux 主机将您的内部网路连接至其它网路(比方说,ineternet)的时候,您有机会允许特定类型的交通,而禁止其它的。 例如,一个封包的表头会包含封包的目的地地址,所以您可以防止封包流向外部网路的某一部份。再如,我用 Netscape 连线至 Dilbert archives,那网页上有一个来自 doubleclick.net 的广告,这样 Netscape 会浪费我的时间去下载它们。只要让封包过滤机制不允许任何来自doubleclick.net 的封包,我们就可以解决这个问题(当然,有更好的方法来做这件事情啦,请参考 Junkbuster)。 <tag/Security:/ 当您的 Linux 主机是您井然有序的内部网路和外面那个混沌无比的 internet 之间的唯一通道,而您知道可以限制哪些东西才能进入您的门户,诚是不错之举吧。例如,您或许会放行任何从内部网路出去的东西,但又担心来自外面的恶名昭章的‘Ping of Death’。又如,您或许并不希望别人从外面 telnet 上您的 Linux 主机,尽管全部的帐号都有密码保护。或许,您还想(正如大部份人一样)在 internet 上当看客而不愿当伺服器(也可能您是愿意的) ,最简单莫如用封包过滤来拒绝任何意欲连线的封包,不让任何人连进来。 <tag/Watchfulness:/ 有时候,一台设定差劣的机器会从本地网路向外面呕送封包。而好消息是您可以让封包过滤来告诉您是否有变态的事情发生。您或许会对之采取行动,又或许早已见惯不怪了。 </descrip> <sect1>如何在 Linux 下过滤封包?<label id="filter-linux"> <p>Linux 的核心自从 1.1 版就已经有封包过滤功能。第一代是 1994 年由 Alan Cox 基於 BSD 的 ipfw 移植过来的,後来在 Linux 2.0 版本再由 Jos Vos 加强,利用 ' ipfwadm ' 这只使用者空间(userspace *)工具来控制核心的过滤规则。在 1998 年年中,我在 Micahel Neuling 的大力帮助下,投注了相当的精力在 Linux 核心 2.2 上面,推出了 ' ipchains ' 这只工具。终於,Linux 核心 2.4 的第四代工具 ' iptables ' 连同其它核心改写也在 1999 年年中进行开发了。这就是目前这个 iptables 的 HOWTO 文件所致力之所在。 <p> (* 译者注:“使用者空间”通常是用来区别系统记忆体的使用□围,主要类型分为核心空间和使用者空间。原作者可能以为大家都是程式高手,故会用如此专门术语。然对一般读者来说,理解上或许有困难,故此多说两句。在往後的阅读中也请留意。) <p> 您需要一个核心有 netfilter 建构於其中:netfilter 是 Linux 核心中一个通用架构,可以让其它东西(例如 iptables 模组) 插入(plug into)。换句话说,您需要核心 2.3.15 或更新的版本,同时在核心编译时以 ' Y ' 回答 CONFIG_NETFILTER 这个选项。 <p> <tt>iptables</tt> 这只工具会和核心对讲并告诉它什麽封包要过滤。除非您是一个程式人员,或异想天开,那您就是用它来控制封包怎样过滤的了。 <sect2> iptables <p> 这只 <tt>iptables</tt> 工具可以插入或移除核心封包过滤表格(packet filtering table) 中的一些规则(rules)。也就是说,无论您设定了什麽,要是重新启动(reboot)系统的话,就会全部丢失;请参阅 <ref id="permanent" name="制定永久性规则(Making Rules Permanent)">, 看看如何确保设定在下次 Linux 启动後可以回存。 <p> <tt>iptables</tt> 是用来取代 <tt>ipfwadm</tt> 和 <tt>ipchains</tt> 的:请参阅 <ref id="oldstyle" name="使用 ipchains 和 ipfwadm (Using ipchains and ipfwadm)">, 看看如何无痛的避免使用 iptables,假如您目前正使用它们其中之一。 <sect2> 制定永久性规则<label id="permanent"> <p>您目前的防火墙设定是储存於核心里面的,也正因如此,设定会在系统重启後丢失。iptables-save 和 iptables-restore * 的撰写目前已经被列入 TODO 列表中了。我保证当它们问世的时候,肯定非常棒。 <p>(* 译者注:在 ipchains 工具中,可以使用 ipchains-save 与 ipchains-restore 来把当前的防火墙设定储存起来,以及将之还原。如果读者没使用过 ipchains 那两个功能的话,或许不知道作者在说什麽。) <p>目前来说,就把设定规则所需的那些命令写进一个初始命令稿(script)中吧。要确定的是,假如有其中一个命令失败的时候,您能提供一些智能的动作反应 (通常如 ' exec /sbin/sulogin' )。 <sect>你算哪根葱啊?为何玩我的核心? <p> 我是 Rusty,是 Linux IP 防火墙的维护者,同时也进行其它的一些编程工作,可以算是天时地利人和的使然吧。我写过 ipchains (请参阅前面的 <ref id="filter-linux" name="如何在 Linux 下过滤封包?(How Do I Packet Filter Under Linux?)">, 看看实际的工作尚得益於哪些同仁),从其中学到足够的东西以匡正今次的封包过滤。我希望如此。 <p> <url url="http://www.watchguard.com" name="WatchGuard"> 是一个非常出色防火墙公司,出售真正好用的随插式防火墙设备(plug-in Firebox),且向我免费提供,让我可以全力撰写这些东西,以及维护过往的一些东西。我原本预估 6 个月就可以了,但实际上却花了 12 个月,不过我在最後阶段觉得做得还不错就是了。多翻重写、硬碟毁坏、手提电脑遭窃、数个档案系统的损毁、以及後来的荧幕坏掉,最终,还是做出来了。 <p> 在这里,我想澄清一些朋友的错误观念:我并非核心(kenrl)里面的专家。我之所以知道这些,是由於某些核心工作让我接触到他们其中的一些成员: David S. Miller、Alexey Kuznetsov、Andi Kleen、Alan Cox。不过,猪头骨(最难的)都由他们啃了,剩下的豆腐(安全和容易的)才轮到我来收拾啦。 <sect> 真正的 Rusty 封包过滤快速指南 <p> 大部分人都仅用单一的 PPP 拨接上网,同时并不想任何人进入他们的网路、或防火墙: <tscreen><verb> ## Insert connection-tracking modules (not needed if built into kernel). # insmod ip_conntrack # insmod ip_conntrack_ftp ## Create chain which blocks new connections, except if coming from inside. # iptables -N block # iptables -A block -m state --state ESTABLISHED,RELATED -j ACCEPT # iptables -A block -m state --state NEW -i ! ppp0 -j ACCEPT # iptables -A block -j DROP ## Jump to that chain from INPUT and FORWARD chains. # iptables -A INPUT -j block # iptables -A FORWARD -j block </verb></tscreen> <sect> 封包如何穿越(traverse)过滤 <p> 核心从 'filter' 表格的三个列表(lists) 开始;这三个列表叫做 <bf>firewall chains(防火墙链)</bf> 或就叫 <bf>chains(链)</bf>。 这三个链分别为<bf>INPUT</bf>、<bf>OUTPUT</bf> 、和 <bf>FORWARD</bf> 。 <p><bf>这跟 2.0 和 2.2 核心有很大差别哦!</bf> <p> 对於 ASCII 艺术迷来说,各链(chains)的布置如下: <verb> _____ Incoming / \ Outgoing -->[Routing ]--->|FORWARD|-------> [Decision] \_____/ ^ | | v ____ ___ / \ / \ |OUTPUT| |INPUT| \____/ \___/ ^ | | ----> Local Process ---- </verb> <p>其中三个圈代表著前述的三个链,当一个封包抵达上图中的其中一个圈,相应的链就会接受检验(examined),以决定那个封包的命运。如果链说 DROP 掉这个封包,那麽它就会就地正法,但如果链说 ACCEPT 这个封包,那麽它就继续在图示中穿越。 <p> 一个链(chain)其实就是众多规则(rules)中的一个检查清单(checklist)。每一条规则都会说“如果封包表头看起来像这样,就如此这般处置这个封包”。如果规则的设定和封包并不符合(match),那麽就交由链中的下一个规则继续处理。而最终,如果再没有规则可以参考,那麽核心就会看链的<bf>policy(原则)</bf> 以决定怎麽做。在一个安全至上的系统里,原则(policy)通常都会告诉核心 DROP 掉该封包。 <p> <enum> <item>当一个封包进入的时候(假设,通过 Ethernet 网路卡),核心首先看看封包的目的地(destination):这称之为 ' rouging (路由)'。 <item>如果目的地址为本机,这个封包就按图示下行至 INPUT 链。如果它能够通过,那麽等待这个封包的行程(processes)就将之接管下来。 <item>否则,如果核心并没启动转递功能(forwarding),或是它不知道如何转递这个封包,那麽这个封包就会被丢弃(dropped)。如果转递功能已经启动,同时封包指向另一个网路界面(如果您还有另外一张),然後这个封包就按图示右行至 FORWARD 链。如果它被接受(ACCEPT),那麽它就会被送出去。 <item>最後一种情形,一个在本机运行的程式会送出网路封包。封包就直接交给 OUTPUT 链:如果是 ACCEPT,然後这个封包会继续送出至它所指向的界面。 </enum> <sect>使用 iptables <p> 如果您需要特定的详细了解,iptables 有一个非常详尽的 manual page (<tt>man iptables</tt>)。假如您熟悉 ipchains 的话,或许可以直接跳到 <ref id="Appendix-A" name="iptables 与 ipchains 的差异 (Differences Between iptables and ipchains)"> 去看;它们是非常近似的。 <p> 您还可以利用 <tt>iptables</tt> 做许多不同的事情哦。您所开始的那三个内建(buit-in) 链: <tt>INPUT</tt>、<tt>OUTPUT</tt>、和<tt>FORWARD</tt> ,您是不能删除的。让我们看看整个链的管理运作吧: <enum> <item> 建立一个新链 (-N)。 <item> 删除一个空链 (-X)。 <item> 改变一个内建链的原则 (-P)。 <item> 列出一个链中的规则 (-L)。 <item> 清除一个链中的所有规则 (-F)。 <item> 归零(zero) 一个链中所有规则的封包字节(byte) 记数器 (-Z)。 </enum> 有好些方法可以统筹一个链中的规则: <enum> <item> 延增(append) 一个新规则到一个链 (-A)。 <item> 在链内某个位置插入(insert) 一个新规则(-I)。 <item> 在链内某个位置替换(replace) 一条规则 (-R)。 <item> 在链内某个位置删除(delete) 一条规则 (-D)。 <item> 删除(delete) 链内第一条规则 (-D)。 </enum> <sect1> 当您的机器启动时,您所看到的 <p> iptables 可以做成模组(module),叫做 `iptable_filter.o' ,当您第一次跑 <tt>iptables</tt> 就会被自动载入。它也可以永久性的建置於核心里面。 <p>在跑任何 iptables 命令之前 (小心:有些套件(distributions) 或许会用它们的起始命令稿来跑 iptables),内建链( `INPUT'、`FORWARD'、和 `OUTPUT' )将不带任何规则,所有链都将原则设为 ACCEPT。您可以将 iptable_filter 模组选项设为 `forward=0' ,来改变预设的 FORWARD 链原则。 <sect1> 一个单一规则的运作 <p> 下面让我们来熟练一下原则的运用吧,所谓熟能生巧是也。您最常用的或许会是 append (-A) 和 delete (-D) 命令。至於其它如 insert (-I) 和 replace (-R), 只是这些概念的延伸而已。 <p> 每一条规则都限定了一组条件(conditions)与特定封包比对,以及当它们符合时要如何处置(指一个`target' )。比方说,您或许要丢弃所有来自127.0.0.1 这个 IP 地址的 ICMP 封包,因而我们这里的条件就成为这样:协定必须是 ICMP,而来源地址必须是 127.0.0.1 ,而我们的 target(目标)将会是`DROP' 。 <p> 我们称 127.0.0.1 为 `loopback' 界面,就算您没有真实的网路连接,您也会有这个界面的。您可以用 `ping' 这只程式产生这样的封包 (它只是送出一个 type 8(echo request)的 ICMP 封包,而所有乐於回应的合作端(cooperative hosts) 则送回一个 type 0(echo reply)的 ICMP 封包)。用来测试是很好用的。 <tscreen><verb> # ping -c 1 127.0.0.1 PING 127.0.0.1 (127.0.0.1): 56 data bytes 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.2 ms --- 127.0.0.1 ping statistics --- 1 packets transmitted, 1 packets received, 0% packet loss round-trip min/avg/max = 0.2/0.2/0.2 ms # iptables -A INPUT -s 127.0.0.1 -p icmp -j DROP # ping -c 1 127.0.0.1 PING 127.0.0.1 (127.0.0.1): 56 data bytes --- 127.0.0.1 ping statistics --- 1 packets transmitted, 0 packets received, 100% packet loss # </verb></tscreen> 这里您可以看到第一个 ping 成功了(这里的 `-c 1' 参数是告诉 ping 只送出一个封包)。 <p> 然後,我们为`INPUT' 延增(-A)一条规则,将来自 127.0.0.1(`-s 127.0.0.1') 的 ICMP 协定 (`-p icmp') 封包送至 DROP 这个目标 (-j DROP)。 <p> 然後我们可以用第二个 ping 来测试我们的规则。在程式放弃继续等待那些永不到来的回应之前,将有一段暂停。 <p> 我们有两个方法可以移除规则。首先,因为我们目前制定在 input 链中只有唯一一条规则,所以我们可以指定数字来移除,例如: <tscreen><verb> # iptables -D INPUT 1 # </verb></tscreen> 这样就把第一条规则从 INPUT 链中移除掉。 <p> 第二个方法是映射(mirro)上面的 -A 命令,但用 -D 来代替 -A 而已。当您有一个链,里面写有非常复杂的规则,而又不想逐行数出第 37 行就是您要的那条规则,这时候,这方法就非常有用了。 <tscreen><verb> # iptables -D INPUT -s 127.0.0.1 -p icmp -j DROP # </verb></tscreen> 在命令行中,其语法是 -D 必须和 -A (或 -I、或 -R) 命令的位置一致。如果在同一个链中有数条相同的规则,那麽只有第一条会被移除掉。 <sect1>过滤规格 <p> 我们已经看过用 `-p' 来指定协定,以及用 `-s' 来指定来源地址,但还有其它选项我们是可以用来指定出一个封包的特征。底下是一个完整的概述。 <sect2>指定来源和目的地之 IP 地址 <p> 我们可以用四种方法来指定来源(`-s'、或`--source'、或 `--src') 和目的地(`-d'、或`--destination'、或`--dst') IP 地址。最常用的方法是使用完整名称,例如 `localhost' 或 `www.linuxhq.com' 。第二种方法是指定其 IP 地址,例如 `127.0.0.1' 。 <p> 第三和第四种方法允许指定一组(group) IP地址,例如 `199.95.207.0/24' 或 `199.95.207.0/255.255.255.0' ,这两个设定都指定了所有从 199.95.207.0 到 199.95.207.255 之间的 IP 地址;而在数字後面的 `/' 符号是告诉系统哪部份 IP 才有效。 `/32' 或 `/255.255.255.255' 为预设值(所有 IP 值都必须吻合)。全部用 `/0' 来指定 IP 地址也是可行的,例如: <tscreen><verb> [ NOTE: `-s 0/0' is redundant here. ] # iptables -A INPUT -s 0/0 -j DROP # </verb></tscreen> 不过这非常少用,因为以上的效果和不指定 `-s' 毫无两样。 <sect2>相反指定 <p> 许多旗标(flags),包括 `-s' (或 `--source')、和 `-d' (或 `--destination'),可以在它们前面放置一个 `!' 符号(发音为`not') ,来符合所有非(NOT)其赋予值的地址。比方说,`-s ! localhost' 符合所有<bf>非(not)</bf> 来自本机的封包。 <sect2>指定协定 <p> 协定可以用 `-p' (或 `--protocol') 旗标来指定。协定可以为一个号码(假如您知道 IP 协定数值的话),或是一个诸如 `TCP'、或`UDP'、或`ICMP' 这样的名称。大小写没关系,所以 `tcp' 和 `TCP' 都可以工作。 <p> 协定也可以加上一个 `!' 前置符号,使之相反。例如 `-p ! TCP' 则指定了所有<bf>非</bf> TCP 的封包。 <sect2>指定界面 <p> 我们用 `-i' (或 `--in-interface') 和 `-o' (或 `--out-interface') 选项来指定一个符合的<bf>界面(interface)</bf>。一个界面就是封包进入(`-i') ,或传出(`-o')之物理设备。您可以用 <tt>ifconfig</tt> 命令列出哪些界面是跑起来(`up' )的。 <p> 穿越 <tt>INPUT</tt> 链的封包不会有传出(output)界面的,所以,任何在链中使用 `-o' 选项的规则都不与之符合。同样的,穿越 <tt>OUTPUT</tt> 链的封包也不会有传入(input)界面,所以在链中任何带 `-i' 选项的规则也是不符合的就是了。 <p>仅仅是穿越 <tt>FORWARD</tt> 链的封包才会同时有传入和传出界面。 <p> 指定一个不存在的界面是完全合法(legal)的;反正在界面还没起来之前,这条规则是不会符合的。这对於 PPP 拨接(通常会是<tt>ppp0</tt>) 或相类连线,就极之有用了。 <p> 例如在一个特殊例子中,界面是用一个 `+' 结尾的话,就泛指所有以此字串开头的界面(不管它们目前是否起来了)。例如,要指定一条规则来符合所有的 PPP 界面的话,<tt>-i ppp+</tt> 选项就可以用上了。 <p> 界面名称前面可以用一个`!' 符号来符合一个与指定界面 <bf>不</bf> 符合的封包。 <sect2>指定封包碎片 (Fragments) <p> 有时候,一个封包会因为太大而不能一次过塞进连线去。当这样的事情发生了,封包会被切割成 <bf>碎片(fragments)</bf>,同时会以多个封包来传送。而另一端则重组这些碎片以还原整个封包。 <p> 但碎片的问题是,第一个起始碎片有整个封包表头栏位(IP+TCP、UDP、和 ICMP)可供检查,但後继封包却只包含表头的小部份(不带额外协定栏位的 IP)。这样的话,要检查後继碎片之协定表头(比方由 TCP、UDP、和 ICMP extensions 而成),就不可能了。 <p> 如果您要做连线追踪或 NAT,那所有碎片在递给封包过滤码之前都会汇合回一起,所以您无需担心碎片问题。 <p> 然而,要弄明白过滤规则如何处理碎片的,就变得非常重要了。任何规则要询问的资料而我们并没有时,将被视为 <em>不</em> 符合。也就是说,第一个碎片封包的处理和其它封包一样。但第二及之後的碎片就不是这样了。这样的话,一条 <tt>-p TCP --sport www</tt> (指定来源埠口为`www')的规则,将永远不和碎片符合(除第一个碎片外)。相反的规则如<tt>-p TCP --sport ! www</tt> 也一样就是了。 <p> 不过,您可以用 `-f' (or `--fragment') 旗标特别为第二及以後的碎片指定一条规则。在 `-f' 前面加上一个 `!' 来指定一条规则 <em>不</em> 适用於第二及以後碎片,也是可行的。 <p> 通常,让第二及以後碎片通过是被视为安全的,因为如果过滤会影响第一个碎片的话,那麽也就可以避免在目标主机进行重组;但是,一些已知的臭虫显示,丢送碎片封包可以轻易的让主机当掉。那是阁下要应付的事情了。 <p> 网路玩家要留意的是:当进行这样的检测时,不完整的封包(太短的 TCP、UDP、和 ICMP 封包会让火墙程式读不到埠口或 ICMP 码和类型) 会被丢弃。因此, TCP 碎片都由第 8 个位置开始的 *。 <p>(* 译者注:我也不是很明白作者这里所指何物,原文是:‘So are TCP fragments starting at position 8’。因为懒得去翻资料,故不知道 position 8 是指 TCP 表头位置还是其它。假如您找到答案,欢迎写信给我以作澄清。) <p> 举例来说,以下的规则会丢弃任何送给 192.168.1.1 的碎片。 <tscreen><verb> # iptables -A OUTPUT -f -d 192.168.1.1 -j DROP # </verb></tscreen> <sect2>延伸 iptables :新的比对(matches) <p><tt>iptables</tt> 是 <bf>可延伸的(extensible)</bf>,也就是说,核心和 iptables 工具可以进行扩展以提供新的功能。 <p> 某些延伸(Extensions)是标准的,但有些则可以说是派生出来的。别的朋友或许会制做出一些延伸,同时散播给合适的用户。 <p>核心的延伸通常居於核心模组目录内,例如 /lib/modules/2.3.15/net 。假如您的核心是用 CONFIG_KMOD 设定来编译的话,它们是应需求载入的,所以您无需手动的插入它们。 <p>然而,iptables 程式的延伸则通常是居於 /usr/local/lib/iptables/ 里面的分享函式库,或者有些散播版本会将它们放进 /lib/iptables 或 /usr/lib/iptables 里去。 <p>延伸有两个种类:新目标(target),和新比对(match);下面我们就讲讲新目标吧。有些协定会自动提供新的测试(tests):目前有 TCP、UDP、和 ICMP,如下述。 <p>在命令後使用 `-p' 选项把延伸载入进来,您就可以来指定一个新测试了。当延伸选项允许的时候,使用 `-m' 来载入延伸,则可以明确指示一个新测试。 <p>如需某个延伸的求助资料,可以使用选项後接 `-h' 或 `--help' 将之载入(`-p'、 `-j'、或 `-m'),例如: <tscreen><verb> # iptables -p tcp --help # </verb></tscreen> <sect3>TCP 延伸 <p>如果指定了 `-p tcp' ,TCP 之延伸会自动载入的。它提供如下选项(并不符合 fragments)。 <p> <descrip> <tag>--tcp-flags</tag> 後接一个 `!' 选项,则有两个旗标的字串让您能够对指定的 TCP 旗标进行过滤。 第一个字串是遮罩(mask):一个您欲检查的旗标列表。第二个字串是要说哪些东西要设定。例如: <tscreen><verb> # iptables -A INPUT --protocol tcp --tcp-flags ALL SYN,ACK -j DENY </verb></tscreen> 这表示所有旗标都要检查 (`ALL' 就是泛指 `SYN,ACK,FIN,RST,URG,PSH'),但只有 SNY 和 ACK 被设定而已。另外有一个参数 `NONE' 则是没旗标的意思。 <tag>--syn</tag> 为`--tcp-flags SYN,RST,ACK SYN' 的简写,其前面可以备选一个 `!' 符号。 <tag>--source-port</tag> 其後可以备选 `!' ,然後是一个单独的 TCP 埠口或一个埠口值域(range)。埠口可以为 /etc/services 所列岛埠口名称,也可以是一个数字。如果是值域的话,可以是一对用`:' 符号分隔的埠口名字,或一个埠口後面带 `:' (指大於和等於该埠口),又或是一个埠口前面带 `:' (指小於和等於该埠口)。 <tag>--sport</tag> 等同於 `--source-port'。 <tag>--destination-port</tag> 和 <tag>--dport</tag> 与上同,只是它们是用来指定目的地而非来源埠口加以比对。 <tag>--tcp-option</tag> 其後可以备选 `!' ,然後为一个数字,用来比对一个 TCP 选项等於该数字的封包。假如需要检查 TCP 选项,那些 TCP 表头不完整的封包就会自动的被丢弃。 </descrip> <sect4>一个 TCP 旗标的解释 <p> 有时候,允许单向而非双向的 TCP 连线会很好用。例如,您或许想允许连线到外部 WWW 伺服器,但却不想来自该伺服器的连线。 <p> 最幼稚的举动或许会是挡掉来自该伺服器的 TCP 封包。但不幸的是,TCP 连线根本就要求封包是双向传递的。 <p> 解决之道是把那些要求连线的封包挡掉。这些封包被称为 <bf>SYN</bf> 封包(嗯,技术上讲,它们是带 SYN 设定的封包,而 FIN 和 ACK 标签则是空白,只是我们将之简称为 SYN 封包而已)。要只限制这样的封包的话,我们就可以制止那些外来的连线尝试了。 <p> `--syn' 旗标可以用於这些方面:它仅对那些指定为 TCP 协定的规则有作用。例如,指定来自 192.168.1.1 的 TCP 连线请求: <tscreen><verb> -p TCP -s 192.168.1.1 --syn </verb></tscreen> <p> 这旗标也可以後接一个 `!' 来反设,意指每一个非该类初始连线的封包。 <sect3>UDP 延伸 <p> 如果 `-p udp' 被指定的话,这些延伸就会自动载入。它提供了 `--source-port'、 `--sport'、`--destination-port'、以及 `--dport' 这些选项,一如前述的 TCP 设定。 <sect3>ICMP 延伸 <p> 如果 `-p icmp' 被指定的话,这个延伸就会自动载入。它只提供一个新的选项: <p> <descrip> <tag>--icmp-type</tag> 其後可以备选 `!' ,然後是一个 icmp 名称类型(如 `host-unreachable' ),或是一个数字类型(如 `3' ),或是一对用 `/' 分隔的数字类型和编码(如 `3/3' )。使用 `-p icmp --help' 就可以获得一份可用 icmp 类型名称清单。 </descrip> <sect3>其它比对的延伸 <p> 在 nerfilter 套件中的其它延伸则是展示性(demonstration)的延伸内容,可以用 `-m' 选项来呼叫(假如已安装了的话)。 <descrip> <tag>mac</tag> 此一模组必须要明确的用 `-m mac' 或 `--match mac' 来指定。它用於比对传入封包的来源 Ethernet (MAC) 地址,因而只对那些穿越 PREROUTING 和 INPUT 链的封包起作用。它只提供一个选项: <descrip> <tag>--mac-source</tag> 其後可以备选 `!' ,然後是一个用冒号分隔的十六进制 ethernet 地址,如 `--mac-source 00:60:08:91:CC:B7'。 </descrip> <tag>limit</tag> 这个模组必须明确的用 `-m limit' 或 `--match limit'来指定。它用来限制一个比对等级,诸如抑制记录信息等。它只能比对一个每秒次数值(预设是每一个小时 3 个比对,伴随 5 个触发(burst))。它接受两个备选参数: <descrip> <tag>--limit</tag> 後接一个数值;指定可允许的每秒最大平均比对数值。该数值可以用 `/second'、`/minute'、`/hour'、或 `/day'、或其中部份 (故 `5/second' 和 `5/s' 是一样的),来明确指定单位(unit), <tag>--limit-burst</tag> 後接一个数值,指示出引起前述限制之前的最大触发次数。 </descrip> 这个比对常用於 LOG 目标,以进行比率限制(rate-limited) 之记录。为了更好了解它是如何工作的,让我们看一看下面的规则,是以预设限制引数来记录封包的: <tscreen><verb> # iptables -A FORWARD -m limit -j LOG </verb></tscreen> 当此规则第一次引用的时候,封包就会被记录下来;事实上,由於预设的触发为 5 ,那为首的 5 个封包就会记录下来。然後,再隔 20 分钟此规则才会再记录封包,而不管期间有多少个封包抵达。而且,每 20 分钟如果没有符合的封包通过,则会恢复 (regained) 一个触发数值;假如 100 分钟内再无这样的封包触及这规则的话,那麽触发次数就会完全复原(recharged);回到我们开始时的状态。 <p>注:您目前不能以大於 59 小时的复原时间来建立一个规则,故此,假如您设定一个平均率为每天一次,那麽,您的触发率则一定要少於 3 。 <p>您也可以用这模组去避免以快速比率提升服务回应的阻断服务攻击(DoS)。 <p>Syn-flood protection: <tscreen><verb> # iptables -A FORWARD -p tcp --syn -m limit --limit 1/s -j ACCEPT </verb></tscreen> Furtive port scanner: <tscreen><verb> # iptables -A FORWARD -p tcp --tcp-flags SYN,ACK,FIN,RST RST -m limit --limit 1/s -j ACCEPT </verb></tscreen> Ping of death: <tscreen><verb> # iptables -A FORWARD -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT </verb></tscreen> 此模组的工作原理有点像“截流阀”一样,请参考下面的图示。 <tscreen><verb> rate (pkt/s) ^ .---. | / DoS \ | / \ Edge of DoS -|.....:.........\....................... = (limit * | /: \ limit-burst) | / : \ .-. | / : \ / \ | / : \ / \ End of DoS -|/....:..............:.../.......\..../. = limit | : :`-' `--' -------------+-----+--------------+------------------> time (s) LOGIC => Match | Didn't Match | Match </verb></tscreen> 比方说,我们以 5 个封包触发来比对每秒一个封包,但封包从每秒四个开始传入,持续三秒,然後等三秒再重新开始。 <tscreen><verb> <--Flood 1--> <---Flood 2---> Total ^ Line __-- YNNN Packets| Rate __-- YNNN | mum __-- YNNN 10 | Maxi __-- Y | __-- Y | __-- Y | __-- YNNN |- YNNN 5 | Y | Y Key: Y -> Matched Rule | Y N -> Didn't Match Rule | Y |Y 0 +--------------------------------------------------> Time (seconds) 0 1 2 3 4 5 6 7 8 9 10 11 12 </verb></tscreen> 您会发现头五个封包被允许超过每秒一个封包,然後就引起限制了,如果有一个停歇,其它的触发也将被允许,但就不能通过规则设定的最高比率(在该触发使用後为每秒一个封包)。 <tag>owner</tag> 此模组为本机产生的封包比对不同特征的封包建立者(creator)。它仅对 OUTPUT 链有用,而且,甚至某些封包(如 ICMP ping responses)或许没有 owner,将被视为不符合哦。 <descrip> <tag>--uid-owner userid</tag> 如果封包由一个行程以有效(数字式) user id 建立的,则为符合。 <tag>--uid-owner groupid</tag> 如果封包由一个行程以有效(数字式) group id 建立的,则为符合。 <tag>--pid-owner processid</tag> 如果封包由一个行程以 process id 建立的,则为符合。 <tag>--sid-owner processid</tag> 如果封包由一个行程以 session group 建立的,则为符合。 </descrip> <tag>unclean</tag> 此一实验性模组必须以 `-m unclean' 或 `--match unclean' 来明确指定。它会对封包进行不同的随机判断检测。这模组尚未被稽查过,所以不应该用於安全设备上(它或许会把事情搞砸,因为它本身或许有臭虫的)。它并没提供选项设定。 </descrip> <sect3>The State Match <p>最有用的比对判断标准由 `state' 延伸所提供,以诠释 `ip_conntrack' 模组的连线追踪分析。这是非常值得鼓励使用的。 <p>指定 `-m state' 则允许另一个额外的 `--state' 选项,可以为一个豆点分隔的比对陈述列表( `!' 旗标指示 <bf>不(not)</bf> 符合那些陈述)。这些陈述是: <descrip> <tag>NEW</tag> 一个建立新连线的封包。 <tag>ESTABLISHED</tag> 一个属於现有连线(如:已经回应封包了)之封包。 <tag>RELATED</tag> 一个与现有连线相关,但却并不限於其中部份的封包,诸如 ICMP 错误,或是建立 FTP 数据连线的封包(FTP 模组已插入)。 <tag>INVALID</tag> 一个因某些原因不能被鉴别的封包:这包括记忆体不足和不能回应任何已知连线的 ICMP 错误。通常,这样的封包都会被丢弃掉。 </descrip> <sect1>目标(Target)规格 <p>现在,我们知道可以对封包做什麽样的检查了,我们还需要一个方法来说出对一个符合我们测试的封包要做什麽样动作。这就是所谓的一条规则之<bf>目标(target)</bf> 啦。 <p>有两个非常相类的内建目标:DROP 和 ACCEPT,我们已经接触过了。如果一条规则符合一个封包,同时目标是其中之一,那麽就再没有规则需要咨询:封包的命运已经定下来了。 <p>除了内建外,也有两种类型的目标:延伸和用户自定链。 <sect2>用户自定链 <p> <tt>iptables</tt> 承袭了 <tt>ipchains</tt> 一个非常厉害的功能,就是让使用者可以创建出新链,附加於三个内建链(INPUT、FORWARD、和 OUTPUT)之外。按惯例,用户自定链用小写以示区别(待会我们会在後面的 <ref id="chain-ops" name="在整链上运作(Operations on an Entire Chain)"> 那里解释如何去建立新的用户自定连) <p> 当一个封包符合一条目标为用户自定链之规则时,封包就会开始穿越用户自定链中的规则。假如该链未能决定出封包的命运,则一旦结束穿越该链後,就会接著当前链中的下一个规则继续穿越下去。 <p> 继续玩玩 ASCII 艺术好了。假设有这麽两条(怪)链:<tt>INPUT</tt> (内建链), 和 <tt>test</tt> (用户自定链)。 <tscreen><verb> `INPUT' `test' ---------------------------- ---------------------------- | Rule1: -p ICMP -j DROP | | Rule1: -s 192.168.1.1 | |--------------------------| |--------------------------| | Rule2: -p TCP -j test | | Rule2: -d 192.168.1.1 | |--------------------------| ---------------------------- | Rule3: -p UDP -j DROP | ---------------------------- </verb></tscreen> <p> 假设一个来自192.168.1.1 的 TCP 封包,要到 1.2.3.4 那里去。它进入<tt>INPUT</tt> 链,并受到 Rule1 的测试 - 但不符合。但是符合 Rule2 ,且它的目标是 <tt>test</tt>,所以下一个要检验的规则将从 <tt>test</tt> 开始。在 <tt>test</tt> 中的 Rule1 符合,但并没有指定目标,所以再检验下一条规则,也就是 Rule2 。不过它并不符合,所以我们已经抵达这条链的末端了。然後我们回到 <tt>INPUT</tt> 链中,也就是我们刚才检验 Rule2 那里,所以我们现在就要检查 Rule3,依然不符合。 <p> 这样,该封包的路径是这样子的: <tscreen><verb> v __________________________ `INPUT' | / `test' v ------------------------|--/ -----------------------|---- | Rule1 | /| | Rule1 | | |-----------------------|/-| |----------------------|---| | Rule2 / | | Rule2 | | |--------------------------| -----------------------v---- | Rule3 /--+___________________________/ ------------------------|--- v </verb></tscreen> <p>用户自定链也可以再跳到另一个用户自定链去(但不要做成回圈:您的封包如果被发现处於回圈中就会被丢弃)。 <sect2>iptables 之延伸:新目标 <p>另一类型的目标是一个延伸。一个目标的延伸由核心模组和可选的 <tt>iptables</tt> 延伸组成,以提供新的命令行选项。在预设的 netfilter 散播版本中有好几个延伸: <descrip> <tag>LOG</tag> 此模组提供核心记录符合的封包。它提供这些额外选项: <descrip> <tag>--log-level</tag> 後接一个层次(level)号码或名称。合法的名称有(大小写有别):`debug'、`info'、`notice'、`warning'、`err'、`crit'、`alert'、以及 `emerg',相对的号码由 7 到 0 。各层次号码的解释请参考 syslog.conf 的 man page。 <tag>--log-prefix</tag> 後接一个最多 30 个字母的字串。此一信息由记录信息开始时送出,令其可以个别的被鉴别出来。 </descrip> 此模组常用於一个限制目标後,所以,您不要灌爆您的记录档哦。 <tag>REJECT</tag> 此模组除了向发送端送出一个 `port unreachable' 这样的 ICMP 错误外,和 `DROP' 是一样的。注:在下列条件中,ICMP 错误信息将不会送出(请参考 RFC 1122): <itemize> <item> 被过滤的封包一开始就是一个 ICMP 错误信息,或是其它不明的 ICMP 类型。 <item> 被过滤的封包为一个无头 (non-head) 碎片。 <item> 我们目前已经送出太多至该目的地的 ICMP 错误信息了。 </itemize> REJECT 另外还接受一个 `--reject-with' 选项来更改其回应封包:请参考说明文件。 </descrip> <sect2>特殊的内建目标 <p>有两种特殊的内建目标:<tt>RETURN</tt> 和 <tt>QUEUE</tt>。 <p><tt>RETURN</tt> 和掉到一个链的末端有相同的效果:对一条内建链的规则而言,则启用该链的原则。对一条用户自定规则而言,则会回到前一个链中继续穿越,就接在跳到这个链的那条规则之後。 <p><tt>QUEUE</tt> 也是一个特殊目标,可以替使用者空间(userspace)行程储列封包。要运用它,两个功能组件是必需的: <itemize> <item>其一为 "queue handler",处理使用者空间与核心之间传送封包的实质机制; <item>另一个为一使用者空间的应用程式,去接收,或说操控,以及对封包做出裁决。 </itemize> IPv4 iptables 的标准 queue handler 为 ip_queue 模组,它目前是以实验性质与核心一起发布的。 <p> 如下是一个如何用 iptables 为使用者空间行程进行储列封包的简单例子: <tscreen><verb> # modprobe iptable_filter # modprobe ip_queue # iptables -A OUTPUT -p icmp -j QUEUE </verb></tscreen> 用此规则,本机产生的对外 ICMP 封包(如用 ping 建立) 就会被送至 ip_queue 模组去,然後尝试将封包传给使用者空间应用程式。如果没有使用者空间应用程式在等待的话,该封包就会被丢弃。 <p>要写一个使用者空间应用程式,需使用 libipq API 。它也是和 iptables 一起发布的。程式码□例可以在 CVS 中的 testsuite 工具(如 redirect.c) 找到。 <p>ip_queue 的状态可以用如下方法来检查: <tscreen><verb> /proc/net/ip_queue </verb></tscreen> 储列的最大长度(如传递给使用者空间且无需送回裁决封包之数量)可以通过这样的方式来控制: <tscreen><verb> /proc/sys/net/ipv4/ip_queue_maxlen </verb></tscreen> 最大储列长度的预设值为 1024。一旦达到此限制,新的封包就会被丢弃,直到储列长度跌回低於限制之数为止。好的协定,如 TCP,会将丢弃的封包解释为拥挤(congestion),同时理想地,当储列填起来後会将之挡回去。然而,如果预设值在所举情形下觉得太小的话,或许需要一些实验来决定其理想的最高储列长度。 <sect1>在整链上运作<label id="chain-ops"> <p> <tt>iptables</tt> 的一个非常有用的功能是,它能够组合(group)相关的规则於链中。只要您喜欢,您可以随便为链起一个名字,但我建议您使用小写字母以避免和内建链及目标搞混了。链名最长可以去到 31 个字母。 <sect2>建立一个新链 <p> 现在就让我们一起建一个新链吧。因为我实在是一个爱幻想的家伙,所以我称之为<tt>test</tt> (哈,有点讽刺)。这里,我们用 `-N' 或 `--new-chain' 选项: <tscreen><verb> # iptables -N test # </verb></tscreen> <p> 就是这麽简单。好了,现在您可以将一些规则加入其中,一如前面说的那样。 <sect2>删除一条链 <p> 要删除一条链也是一样简单,用 `-X' 或 `--delete-chain' 即可。为什麽用 `-X' 呢?嗯, 好用的字母都一早给用光了啦。 <tscreen><verb> # iptables -X test # </verb></tscreen> <p> 要删除一条链的话,会有好些限制:它们必需是空的 (请参考後面的 <ref id="flushing" name="清空一条链(Flushing a Chain)"> ) ,同时它们必需不能作为任何规则的目标。任何三条内建链您都不能删除就是了。 <p> 假如您不指定一条链,那麽如果可能的话, <em>全部</em> 用户自定点链都会被删除。 <sect2> 清空一条链<label id="flushing"> <p> 有一个简单的方法可以清空一条链中的所有规则,就是使用 `-F' (或 `--flush') 命令。 <tscreen><verb> # iptables -F forward # </verb></tscreen> <p> 如果您不指定是哪一条链,那麽 <em>全部</em> 链都会被清空。 <sect2>列示一条链 <p> 您可以使用 `-L' (或 `--list') 命令列示一条链中的所有规则。 <p> 每一个用户自定链所列的 `refcnt' ,是说有多少数目的规则是以该链为目标的。在该链被删除之前,这数目必需为零(同时链是空的)。 <p> 如果没提供链名称的话,所有链都会被列示出来,就算空链也一样。 <p> 有三个选项可以伴随 `-L' 一起使用的。首先是 `-n' (numeric) 选项,它很有用,因为它可以避免 <tt>iptables</tt> 去尝试查找 IP 地址,假如您的 DNS 没有设定正确的话,或是您已经过滤掉 DNS 请求了,这或许会造成严重的延迟(假设您和大多数人一样都是使用 DNS )。它同时也会将 TCP 与 UDP 埠口显示为数字而非名称。 <p> 第二个是 `-v' 选项,它会显示出您全部规则的细节,诸如封包的 byte 流量统计、TOS 比较、以及界面等。否则这些数值是被略掉的。 <p> 注:封包的 byte 流量统计可以分别使用 `K', `M' 或 `G' 这些字尾,分别代表 1000、1,000,000、以及1,000,000,000,来显示。使用 `-x' (expand numbers) 旗标同样也可以显示出完整的数字,根本不理会它们有多长。 <sect2>重设(归零)流量记数器(counter) <p> 能够重设流量记数器当然是有用的。您可以用 `-Z' (或 `--zero') 选项来做。 <p> 唯一麻烦是,有时候在进行重设之前,您必需立即记住流量统计值。在前面的例子中,当您下 `-L' 然後 `-Z' 命令,某些封包可能会在这期间通过。因此,您可以把 `-L' 和 `-Z' <em>一起</em> 使用,在读取的同时进行记数器重设。 <sect2>设定原则(policy)<label id="policy"> <p> 我们在前面探讨封包如何通过一个链的时候,已诠释过当封包抵达内建链末端时将会发生什麽事情。此时,就由该链的<bf>原则</bf>来决定封包的命运。只有内建链(<tt>INPUT</tt>、<tt>OUTPUT</tt>、以及 <tt>FORWARD</tt>) 才有原则设定,因为,如果一个封包掉至一个用户自定链的时候,则会回到上一个链中继续穿越。 <p> 原则可以为 <tt>ACCEPT</tt> 或 <tt>DROP</tt>。 <sect> 使用 ipchains 与 ipfwadm<label id="oldstyle"> <p> 在 netfilter 套件中,有两个模组分别叫做 ipchains.o 和 ipfwadm.o。您只要将其中一个插入进核心里面( 注:它们和 iptables.o、ip_conntrack.o 及 ip_nat.o 是不兼容的!)。然後您就可以如往常一般使用 ipchains 或 ipfwadm 了。 <p> 这在一定时期内这仍会被支持的。我认为合理的计算公式是:2 * [ 替代产品发布 - 初始稳定发行 ] ,再加上替代产品可以真正稳定发行的日子。 <p>换而言之,对 ipfwadm 的最後支持将会延至: <tscreen><verb> 2 * [October 1997 (2.1.102 release) - March 1995 (ipfwadm 1.0)] + January 1999 (2.2.0 release) = November 2003. </verb></tscreen> <p> 而对 ipchains 的最後支持则为: <tscreen><verb> 2 * [August 1999 (2.3.15 release) - October 1997 (2.2.0 release)] + July 2000 (2.4.0 release?) = March 2004. </verb></tscreen> 所以,在 2004 年之前都可以高枕无□啦。 <sect> 整合 NAT 与 Packet Filtering <p>要做 Network Address Translation (请参阅 NAT HOWTO) 以及封包过滤,已是很平常之事了。好消息是,将它们混合起来使用实是完全没问题的。 <p>当你设计封包过滤的时候,可以完全不用理会您要做怎样的 NAT 。於封包过滤中看到的来源与目的地,只会是 `真正的' 来源和目的地。举例来说,如果您做 NAT ,要将所有连到 1.2.3.4 port 80 的连线送到 10.1.1.1 port 8080 去,这样封包过滤会看那些送到 10.1.1.1 port 8080 (真正的目的地),而不是 1.2.3.4 port 80。类似的,您也可以忽略封包伪装:封包会看起来是来自真正的内部 IP 地址(比方 10.1.1.1),回应也看起来送回那里。 <p>您可以运用 `state' 比对延伸(match extension)而无需让封包过滤做额外的工作,因为无论如何, NAT 都会要求连线追踪。为了增强在 NAT HOWTO 里面那个简单的封包伪装例子,去挡掉来自 ppp0 界面的任何新连接,您可以这样做: <tscreen><verb> # Masquerade out ppp0 iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE # Disallow NEW and INVALID incoming or forwarded packets from ppp0. iptables -A INPUT -i ppp0 -m state --state NEW,INVALID -j DROP iptables -A FORWARD -i ppp0 0 -m state --state NEW,INVALID -j DROP # Turn on IP forwarding echo 1 > /proc/sys/net/ipv4/ip_forward </verb></tscreen> <sect> iptables 与 ipchains 的差异<label id="Appendix-A"> <p> <itemize> <item> 首先,内建链名称从小写还换成大写,因为 INPUT 与 OUTPUT 链目前只会抓目标为本机以及从本机产生的封包。它们分别用来查看传入与传出的封包。 <item> 现在有一个 `-i' 旗标来代表传入界面,并且只工作於 INPUT 和 FORWARD 链中。在 FORWARD 与 OUTPUT 链中就要将 `-i' 改成 `-o' 了。 <item> TCP 与 UDP 埠口现在都要用 --source-port 或 --sport 选项来拼写出来(或是掉过来写 --destination-port 或 --dport),同时,必需置於 `-p tcp' 或 `-p udp' 选项之後,因为 TCP 或 UDP 延伸是分开载入的。 <item> 以前 TCP 那个 -y 现在变成 --syn,并且必需置於 `-p tcp' 之後。 <item> 原来的 DENY 目标现在终於变成 DROP 了。 <item> 在列示其工作的同时可以将该链归零(zeroing)。 <item> 归零内建链也可以清掉原则记数器(policy counters)。 <item> 列示链可以让您把记数器变成微小快照(atomic snapshot)。 <item> REJECT 与 LOG 现在变成延伸目标了,意味著它们已经和核心模组分开。 <item> 链名称最长可达 31 个字母。 <item> MASQ 现在变成 MASQUERADE, 而且使用不同的语法。REDIRECT 在保留相同名称的同时,也经历了语法的变迁。至於如何设定它们的详细资料,请参阅 NAT-HOWTO。 <item> 而 -o 选项则不再用来将封包传递给使用者空间设备了(参考前面的 -i )。现在则用 QUEUE 目标将封包送给使用者空间。 <item> 哦,我可能已记不得那麽多了。 </itemize> <sect> 关於设计封包过滤的建议 <p> 在电脑安全战场上最明智之举莫过於先挡掉一切,然後开放必需的。有一句至理名言是:`非请勿进'。我建议您牢记於心,假如您最注重安全的话。 <p>不要跑那些您用不到的服务,不管您是否以为已经将之挡下来了。 <p>如果您要建立一个指定式防火墙(dedicated firewall),开始不要跑任何东西,同时挡掉所有封包,然後增加服务以及让所需的封包通过。 <p>我特别强调安全性:结合 tcp-wrappers(对於封包过滤本身的连接)、服务代理(对於通过封包过滤的连接)、路由验证、以及封包过滤等手段。路由验证是指,那些来自未预期界面的封包就会被丢弃:举例说,如果您的内部网路有一段 10.1.1.0/24 的地址,同时有一个来自该地址的封包却从外部界面进入,那它就会被丢弃掉。它可以为一个界面(如 ppp0) 设起来,如: <tscreen><verb> # echo 1 > /proc/sys/net/ipv4/conf/ppp0/rp_filter # </verb></tscreen> 或是全部现有及将有的界面,如: <tscreen><verb> # for f in /proc/sys/net/ipv4/conf/*/rp_filter; do # echo 1 > $f # done # </verb></tscreen> Debian 在可能之下预设就会如此了。如果您有不对称路由(例如,您预期封包会从其它方向进入),您应该在那些界面上关闭此一过滤。 <p>设定防火墙的时候,假如有某些东西不工作的话,记录功能就显得很有用了;但在一个实际运作的防火墙上,任何时候都要将它结合 `limit' 比对来一起使用,以避免有人灌爆您的记录档。 <p>我强烈建议对安全系统做连线追踪:它虽然会引致一些负担(因为所有连线都要追踪),但对於贵网路的连接控制却很有用。如果您的核心不会自动载入模组的话,您或许需要载入`ip_conntrack.o' 模组。假如您要精确追踪复杂的协定,您还需要载入合适的 helper 模组(如,`ip_conntrack_ftp.o' )。 <tscreen><verb> # iptables -N no-conns-from-ppp0 # iptables -A no-conns-from-ppp0 -m state --state ESTABLISHED,RELATED -j ACCEPT # iptables -A no-conns-from-ppp0 -m state --state NEW -i ! ppp0 -j ACCEPT # iptables -A no-conns-from-ppp0 -i ppp0 -m limit -j LOG --log-prefix "Bad packet from ppp0:" # iptables -A no-conns-from-ppp0 -i ! ppp0 -m limit -j LOG --log-prefix "Bad packet not from ppp0:" # iptables -A no-conns-from-ppp0 -j DROP # iptables -A INPUT -j no-conns-from-ppp0 # iptables -A FORWARD -j no-conns-from-ppp0 </verb></tscreen> <p>建置一个良好的防火墙已经超出这个 HOWTO 的□围了,但我的建议是: `一切从严(always be minimalist)'。对於在您机器上进行测试与探索的更多资料,就要参考 Security HOWTO 了。 </article>