此文档描述在Linux2.4 内核中,如何使用iptables过滤不正确的包
Linux 2.4 Packet Filtering HOWTO 简体中文版
Rusty Russell, mailing list
[email protected]
$Revision: 1.3 $ $Date: 2002/06/05 13:21:56 $
简体中文:洋鬼鬼·NetSnake
感谢 网中人
[email protected] 提供的繁体参照
此文档描述在Linux2.4 内核中,如何使用iptables过滤不正确的包
(译者:Packet在很多专业书籍中译为分组,此处根据大部分人的习惯,仍译为包)
1. 简介
2. 官方站点及邮件列表
3. 那么,什么是Packet Filter?
3.1 我为什么需要Packet Filter?
3.2 如何在Linux下进行包过滤?
3.2.1 iptables
3.2.2 创建永久性规则
4. 你算老几,凭什么玩弄我的内核?
5. Rusty的真正的包过滤快速指南
6. 包是如何穿过过滤器的
7. 使用iptables
7.1 当计算机启动后你会看到的
7.2 对单个规则的操作
7.3 过滤规格
7.3.1 指定源和目的IP地址
7.3.2 反向指定
7.3.3 协议指定
7.3.4 接口指定
7.3.5 分片指定
7.3.6 iptables扩展:新的匹配
7.3.6.1 TCP 扩展
7.3.6.1.1 TCP标志的解释
7.3.6.2 UDP 扩展
7.3.6.3 ICMP扩展
7.3.6.4 其他匹配的扩展
7.3.6.5 状态匹配
7.4 目标规格
7.4.1 用户定义链
7.4.2 iptables扩展:新目标
7.4.3 特殊的内建目标
7.5 对整个链进行操作
7.5.1 创建新链
7.5.2 删除链
7.5.3 清空一个链
7.5.4 对链进行列表
7.5.5 重置(清零)计数器
7.5.6 设置原则(默认规则)
8. 使用ipchains和ipfwadm
9. NAT和包过滤的混合使用
10. iptables和ipchains之间的差别
11. 对制定包过滤器的建议
1. 简介
欢迎,亲爱的读者。
这篇文章假设你知道有关IP地址、网络地址、网络掩码、选路和DNS。如果不知道,我建议你先阅读网络概念的HowTo(Network Concepts HOWTO)。
这篇HOWTO并非一个简要的介绍(会让你发热、发毛,没有安全感),也非一个完全的原始的披露(最吃苦耐劳的人也会被搅晕,不过必定会有所斩获)。
你的网络并不安全。问题在于,必须获取快速、简洁的通讯,但又必须限于良好的、无恶意的行为,就如同在嘈杂的大戏院里,你可以高谈阔论,但是绝不能大喊:着火了!。这篇HOWTO不能解决这种问题。
(译者:所有安全都只是相对的,否则根本不会产生这种东西了)
因此,你只能决定在哪方面妥协。我想帮助你使用一些可用的工具和一些通常需要注意的漏洞,希望你将它们用在好的一面,而不是出于恶意的目的 -- 另一个同样重要的问题。
(C) 2000 Paul `Rusty' Russell. Licenced under the GNU GPL.
2、 官方站点及邮件列表位置
这里有三个官方站点:
o Thanks to Filewatcher
http://netfilter.filewatcher.org.
o Thanks to The Samba Team and SGI
http://netfilter.samba.org.
o Thanks to Harald Welte
http://netfilter.gnumonks.org.
你可以通过以下站点访问全部相关站点。
http://www.netfilter.org and
http://www.iptables.org
以下是netfilter官方邮件列表
http://www.netfilter.org/contact.html#list.
3.那么,什么是包过滤器?
包过滤器是这样一种软件:它检查通过的每个包的头部,然后决定如何处置它们。可以这样对待它们:丢弃(也就是说,如果这个包从未被接受,那么丢弃它),通过(也就是说,让包通过),或者更复杂的(操作)。
Linux下,包过滤内建在内核中(内核模块,或者内建),而且我们还有处理包的一些技巧,不过检查头部和处理包的一般性原则仍在这里。
3.1 我为何要包过滤?
控制、安全、警戒。
控制:
当你用你的Linux服务器把你的内部网和另一个网络(就是Internet吧)连起来,你可以决定哪些通信是允许的,哪些不允许。例如,包头部包含了包的目标地址,你可以阻碍包发送到(你)确定的几个外部网络,另一个例子,我用NetScape连接到Dilbert archives。页面上有来自doubleclick.net的广告,然后NetScape浪费了我的时间愉快的下载他们。 告诉包过滤器禁止任何来自或者发往doubleclick.net地址的包,问题就解决了。(当然有更好的办法,见Junkbuster)。
安全:
当Linux服务器是混乱的Internet和你良好的、有序的网络之间唯一的东西时, 你最好能知道哪些东西可以进入你的大门。例如,你可以允许所有(包)从你的网络 发出,不过你可能会为来自外部的著名的“Ping of Death”而焦急。另一个例子,你不希望 外人telnet到你的Linux服务器,尽管所有账户都有密码。或许你只想(像绝大多数人)成为 Internet的旁观者,而非它的服务器(也可能愿意是吧)。简单的不允许任何人接入,设置 包过滤器拒绝所有进入的包(是不错的办法)。
警戒:
有时,本地网络上错误配置的机器可能会向外部喷射出大量的包。最好是当(网络中)出现任何不正常现象时,让包过滤器告诉你。这样你可能可以做点什么,或者你天生就很好奇。
3.2 如何在Linux下进行包过滤?
Linux内核在其1.1系列中就有了包过滤功能。第一代,由Alan Cox 1994年移植于BSD的ipfw。这在Linux 2.0中由Jos Vos和其他人进行了加强;用户空间工具'ipfwadm'可用来控制内核过滤规则。1998年中,我在Michael Neuling的帮助下,为Linux 2.2进行了重写,推出了用户空间工具'ipchains'。最后,1999年中,基于Linux 2.4的第四代工具,'iptables',和其他内核的改写正式推出。这就是这个iptables的HOWTO文档的所在。
译者:userspace根据台湾同胞的说法,是用来区别系统内存中的适用范围的,分为核心空间和使用者空间,不必深究)
你需要包含netfilter架构的内核。netfilter是Linux中的一个通用框架,也可以插入(plug in)其他内容(如iptables模块)。也就是说你需要2.3.15及以后版本,而且在配置内核时对CONFIG_NETFILTER回答'Y'。
iptables这个工具用来和内核交互并告诉它哪些包应该过滤。除非你是程序员或者 特别好奇,否则这就是你用来控制包过滤的了。
3.2.1. iptables
iptables工具向内核的包过滤表中插入和删除规则。这就意味着无论怎样设置,启动后信息都会丢失;请参看“制定永久性规则”(Making Rules Permanent)来确定如何保证下次启动这些规则能被恢复。
iptables是ipfwadm和ipchains的替代品。如果你是它们的使用者,请参看 “使用ipchains和ipfwadm”,如何轻松使用iptables。
3.2.2 创建永久性规则
你当前的防火墙设置保存在内核中,所以重启后就会丢失。你可以试着用iptables-save和iptables-restore脚本来保存他们,并由一个文件恢复。
4. 你算老几,凭什么玩弄我的内核?
我是Rusty Russell。Linux IP防火墙的维护者,也是一个适当的时候出现在适当的地方的coder。我写了ipchains(参见“如何在Linux下进行包过滤?”看看实际的工作其实由哪些人完成),并希望能学到足够的东西修正这次的包过滤。
WatchGuard,一个非常出色的防火墙公司,总之一堆广告,此处省略一千字……
在此,我想澄清一个误解:我不是内核专家,我了解它,是因为我的核心工作让我接触了他们:David S. Miller, Alexey Kuznetsov, Andi Kleen, Alan Cox。无论如何,他们做了最深层的工作,轮到我时,已经非常安全和容易了。
5. Rusty的真正的包过滤快速指南
绝大部分人只有一个PPP连接到Internet,而且不希望有人由此进入他们的网络或者防火墙:
# 插入connection-tracking模块(如国内建在内核中就不需要)
# insmod ip_conntrack
# insmod ip_conntrack_ftp
# 对创建大量新的连接创建一个链,除非这些连接来自内部。
# 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
# 由INPUT和FORWARD链跳往(刚刚创建的)那条链。
# iptables -A INPUT -j block
# iptables -A FORWARD -j block
6. 包是如何穿过过滤器的
内核由'filter'表中的以下三个规则开始。这些被称为防火墙链或就叫链。这三个链分别是 INPUT、OUTPUT和FORWARD。
对于ASCII艺术迷来说,链好象这样:(注意:这与2.0和2.2内核非常不同)
译者:ASCII艺术,这里指的是利用纯ASCII文本作图
_____
Incoming / Outgoing
-->[Routing ]--->|FORWARD|------->
[Decision] _____/ ^
| |
v ____
___ /
/ |OUTPUT|
|INPUT| ____/
___/ ^
| |
----> Local Process ----
三个圈代表上面说的三个链。当包到达图中的一个圈,那个链就检查并确定包的命运。 如果链决定DROP包,包在那里就被杀死。但是如果链决定让包ACCEPT,包就继续在图中前进。
一个链是规则的列表。每个规则都会说:'如果包头看上去像这个的话,那么就这样处理'。 如果规则和包不匹配,由链中的下一个规则处理。最后,如果再也没有要进行处理的规则了, 内核就根据链的原则(policy,有时称为默认规则)来决定应当如何做。在一个注重安全的 系统中,原则通常是让内核丢弃这个包。
1. 当一个包进入时(就是由以太网卡),内核首先检查包的目的地。这被称作“选路”。
2. 如果它就是进入本机的,包会向图中的下方移动,到达INPUT链。如果到了这里,任何等待这个包的进程都会收到它。
3. 否则,如果内核未被允许转发,或者不知道该如何转发这个包,它会被丢弃。如果允许转发,而且包的目的地是另一个网络接口(如果你有另一个的话),那么包向我们图中的右边行进,到达FORWARD链。如果允许通过(ACCEPT),它就被送了出去。
4. 最后,服务器上运行的程序可以发送网络包。这些包马上通过OUTPUT链。如果被允(ACCEPT),那么包继续向可以到达它的目的地的网络接口发送。
7. 使用iptables
iptables有着非常详尽的使用手册(man iptables),而且如果你需要某个选项更详细的介绍。看看“iptables和ipchains的差别”可能对你非常有用。
使用iptables你可以做很多不同的事。开始的内建的三个链INPUT、OUTPUT和FORWARD是不能被删除的。让我们看看整个链的管理。
1. 创建一个新的链 (-N)。
2. 删除一个空链(-X)。
3.修改内建链的原则(-P)。
4. 显示链中的规则(表)(-L)。
5. 清空一个链(-F)。
6. 将链中所有规则的包和字节计数器清零(-Z)。
有几种办法操作链中的规则:
1. 向链中添加一条新规则(-A)。
2. 在链中某个位置插入一条新规则(-I)。
3. 替换某个位置的规则(-R)。
4. 删除链中某个位置的规则,或者是第一个被匹配的。(-D)。
7.1. 当计算机启动后你会看到的
ptables可以作为模块,称为'iptables_filter.o,可以在第一次运行iptables时自动被装载。也可以永久性的编到内核中。
在所有iptables命令执行之前(当心:某些发布版会在初始化脚本中运行iptables),所有内建链中都没有任何规则('INPUT'、'FORWARD'和'OUTPUT'),所有链的原则都是ACCEPT。你可以在装载iptable_filter模块时,提供'forward=0'选项来修改FORWARD的默认原则。
7.2. 对单个规则的操作
这是基本的包过滤:管理规则,添加(-A)和删除(-D)命令可能是最常用的。其他的(-I插入和-R替换)只是简单的扩展而已。
每个规则都有一组条件来匹配包,和如果匹配了该如何做(target)。例如,你可能希望丢弃所有来自127.0.0.1的ICMP包。这样我们的条件就是协议必须是ICMP,而且源地址必须是127.0.0.1,我们的目标是丢弃(DROP)。127.0.0.1是一个回送接口,即使你没有真正的网络连接它也会存在。你可以用ping程序生成这样的包(它简单的发送ICMP 类型8(echo request),所有愿意响应的主机都会用ICMP 类型0(echo reply)来响应)。这对于测试非常有用。
# 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
这里,第一个ping是成功的('-c 1'告诉ping只发送一个包)
然后我们可以向'INPUT'链中添加(-A)一个规则,制定来自127.0.0.1('-s 127.0.0.1')的ICMP协议('-p icmp')包都将被丢弃('-j DROP')。
然后我们测试我们的规则,用第二个ping。在程序放弃等待永远不可能的响应之前,会暂停一下。
我们可以用两种办法中的任一种删除规则。首先,因为知道这是INPUT链中唯一的规则,我们用编号删除:
# iptables -D INPUT 1
删除INPUT链中的编号为1的规则
第二种办法是 -A 命令的映射,不过用-D替换-A。当你的链中规则很复杂,而你不想计算它们的编号的时候这就十分有用了。这样的话,我们可以使用:
# iptables -D INPUT -s 127.0.0.1 -p icmp -j DROP
-D的语法必须和-A(或者-I或者-R)一样精确。如果链中有多个相同的规则,只会删除第一个。
7.3 过滤规格
我们已经看了,用'-p'指定协议,用'-s'指定源地址,不过还有其他选项我们可以用来指定包的特征。下面是一个详细的手册。
7.3.1 指定源和目的IP地址
源('-s','--source'或'--src')和目的('-d','--destination'或'--dst')IP地址可以用四种办法指定。最常用的方法是使用全名,就像'localhost'或者'www.linuxhq.com'。第二种办法是指定IP地址,如'127.0.0.1'。
第三和第四种办法允许指定一组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地址,像这样:
# '-s 0/0'在这里是多余的
# iptables -A INPUT -s 0/0 -j DROP
这很少用到,这和上面出现过的不指定'-s'结果完全一样。
7.3.2 反向指定
很多标记,包括'-s'(或'--source')和'-d'('--destination')标记可以在前面加上'!'标志(读作'not'),来匹配所有和给出的 NOT 的地址。例如, '-s ! localhost'匹配所有不是来自本机的包。
7.3.3 协议指定
可以用'-p'(或'--protocol')指定协议。协议可以是数字(如果你知道IP的协议数值)或者像'TCP'、'UDP'或者'ICMP'这类的名称。大小写无所谓,所以'tcp'和'TCP'一样。
协议名称前可加上'!',以反向解释它,例如'-p ! TCP'将匹配所有不是TCP的包。
7.3.4 接口指定
'-i'(或'--in-interface')和'-o'(或'--out-interface')选项指定匹配的接口名。接口可以是包进入的('-i')或者送出('-o')的物理设备。你可以用ifconfig命令列出当前'up'的接口。(也就是说正在工作的)。
通过INPUT链的包不会有送出接口,所以在这个链中'-o'永远不会匹配。同样,通过OUTPUT链的包也没有进入接口,这个链中的'-i'也不会被匹配。
只有通过FORWARD链的包才有进入和送出两个接口。
可以指定一个当前不存在的接口。在这个接口可用之前,规则不能匹配任何东西。这对于拨号PPP连接及类似的非常有用(通常是ppp0接口)。
一个特殊情况,接口名后面是一个'+',那就会匹配以这个字符串开头的所有接口(无论当前是否存在)。例如,指定一个匹配所有ppp接口的规则,要用到-i ppp+选项。
接口名也可以在前面插入 '!',来匹配所有与指定接口不同的包,如-i ! ppp+。
7.3.5 分片指定
译者:为帮助大家理解,此处附上IP数据报的格式,摘自《Internetworking with TCP/IP》
0 4 8 16 19 24 31
版本号 首部长度 服务类型 总长度
标志符 标志 分片偏移量
寿命 协议 首部效验和
源IP地址
目的IP地址
IP选项 填充
数据
……
有时一个包太大,不可能适合所有线路。这样的话,包会被分成片,然后当作多个包发送。最终重组这些分片来重建整个包。
分片的问题是,被检查的初始片含有整个头部字段(IP+TCP,UDP和ICMP),但随后的包只有一部分头(没有附加协议字段的IP),因此,检查后面的分片的头部(就像有TCP、UDP和ICMP一样)是不可能的。
如果你在做NAT或连接追踪,那么所有分片在包过滤代码处理以前都会合并,所以你不需要为分片担心。
还请注意,到filter表中的INPUT链(或者任何由NF_IP_LOCAL_IN钩子程序钩入的表)的包实际上由核心IP栈片重组后到达。
否则,理解分片是如何被过滤规则处理的就非常重要了。任何过滤规则要求我们没有的信息,将被认为不匹配。这意味着(分片的)第一片像普通的包一样被处理。第二及后面的片则不会。因此,规则 -p TCP --sport www(指定源端口为'www')永远不会匹配一个分片(的包)(除了第一片),相反的规则 -p TCP --sport ! www也不会。
无论如何,你可以用'-f'(或'--fragment')标记指定专门处理第二及以后的分片的规则。当然也可以指定一个规则,让它不去匹配第二及以后的分片,在'-f'前加上'!'。
通常,让第二及以后的分片通过被认为是安全的,因为如果过滤处理了第一片,那么就无法在目标主机上进行重组。不过,已知的Bug是发送分片可能会轻易的让主机崩溃。你自己看着办吧。
网络高手注意:当这类检查进行时,畸形的包(防火墙读取的ICMP代码和类型过短的TCP、UDP和ICMP包)都将被丢弃。所以TCP分片从位置8开始。(译者:什么意思?大概是指IP包中的首部字段位置)
例如,下面的规则会丢弃任何发往192.168.1.1的分片。
# iptables -A OUTPUT -f -d 192.168.1.1 -j DROP
7.3.6 iptables扩展:新的匹配
iptables是可扩展的,也就是包括内核和iptables工具都可以扩充新的特性。
下列部分扩展是标准的,其他的则是派生的。其他人可以做出扩展并发布给合适的人。
内核扩展一般位于内核模块子目录,诸如/lib/modules/2.4.0-test10/kernel/net/ipv4/netfilter。如果你使用了CONFIG_KMOD设置来编译内核,那么它们要求被装载,所以你不需要手工插入。
iptables程序扩展通常是位于/usr/local/lib/iptables/下的共享库,当然也可能在/lib/iptables或者/usr/lib/iptables,具体的要根据不同的发行版本来确定。
扩展有两种:新的目标,新的匹配(我们马上会谈到新的目标)。有些协议自动给出新的测试:如下所示,现有的包括TCP、UDP和ICMP。
这样,你可以在命令行中在 '-p'选项后指定新的测试,就可以载入扩展(模块)了。当允许扩展时,可以用'-m'选项装入扩展。
在选项后面('-p','-j'或者'-m')加上 '-h'或'--help'来获取扩展的帮助。
# iptables -p tcp --help
7.3.6.1. TCP 扩展
如果指定了'-p tcp',那么TCP扩展将自动加载,并提供下列选项(不匹配分片)。
--tcp-flags
可附加一个'!'。有两个标志字串可以通过TCP标记来过滤。第一个标志字符串是mask:你想要测验的标志列表。第二个指出哪些将要被设置。例如:
# iptables -A INPUT --protocol tcp --tcp-flags ALL SYN,ACK -j DROP
意思是所有标志都将被测试('ALL'和'SYN, ACK,FIN,RST,URG,PSH'同义),不过只设置SYN和ACK。当然也可以用'NONE'表示无标志。
--syn
前面的'!'是可选的,是'--tcp-flags SYN, RST, ACK, SYN'的缩写
--source-port
后面可以跟一个'!',可以是单个TCP端口,或一段端口。可以是/etc/services中的端口名或者数字。端口范围格式是低端口名 : 高端口名,或者(指定大于或等于给出的端口)是端口名 + :,或者(指定小于或等于给出的端口)是: + 端口名。
--sport
就是 '--source-port'。
--destination-port
--dport
和上面类似,不过是指定匹配的目的端口(范围)。
--tcp-option
可以跟一个'!'和一个数字,匹配的是TCP选项和数字相等的包。如果试图用 这个TCP选项匹配一个没有完整的TCP包头的包,那么这个包会被自动丢弃。
7.3.6.1.1. TCP标志的解释
有时只允许单向的TCP连接会很有用。例如,你可能会允许连接到外部WWW服务器,但不会允许来自那个服务器的连接。
最简单的举动可能是阻止来自那个服务器的包,可惜,TCP连接需要包双向传送(才能正常工作)。
解决办法是,只阻挡那些用来请求连接的包。这些包称为SYN包(OK,从技术上说,它们的SYN标志被设置,而没有设置RST和ACK标志,不过我们简单的称为SYN包)。通过只阻止这种包,我们就可以阻止来自那些地方的连接企图。
'--syn'标志是这样用的:只对指定了TCP协议的规则有效。例如,指定来自192.168.1.1的连接请求。
-p TCP -s 192.168.1.1 --syn
当然也可以在前面加上'!',意即所有不是初始连接的包。
7.3.6.2 UDP 扩展
这些扩展在指定'-p udp'时自动加载。可以提供 '--source-port'、'--sport'、'--destination-port'和'--dport'等和TCP类似的选项。
7.3.6.3 ICMP扩展
这些扩展在指定'-p icmp'时自动加载。只提供一个新的选项:
--icmp-type
可以跟'!',icmp类型名称(如'host-unreachable')或者数值(如'3'),或者数值类型/代码(如'3/3')。用'-p icmp --help'可以列出可用的icmp类型名。
7.3.6.4 其他匹配的扩展
这些netfilter包中的其他扩展尚属于演示阶段,(如果安装了的话)可以用'-m'来启用。
mac
--mac-source
可以跟一个'!',后面是以太网地址,用冒号分隔的16近制表示,如`--mac-source 00:60:08:91:CC:B7'。
limit
此模块必须明确指定'-m limit'或'--match limit'。用来限制匹配的速率。就像抑制记录信息。只会匹配给定的数字/每秒(默认是每小时3个匹配,和5个触发)。可以有两个参数:
--limit
后面跟数字:指定每秒钟允许的匹配最大平均数。这个数字可以指定 明确的单位,使用'/second'、`/minute'、`/hour' 或者 `/day',或者 只写一部分(如'5/second'和'5/s'一样)。
--limit-burst
后面跟一个数字,指明在上面的limit起作用前最大的触发值。
这个匹配(项)通常和LOG目标结合起来使用,以对速率限制进行记录。 为了理解它是如何工作的,我们来看看下面这条规则,它使用默认限制参数 记录包。
# iptables -A FORWARD -m limit -j LOG
当这条规则第一次启用时,包开始被记录。实际上,由于默认触发是5,前五个包会被记录。然后,每隔20分钟再记录一次包,无论这期间有多少包到达。而且,每个不匹配包的20分钟间隔里,会恢复一个触发(值)。如果100分钟都没有包到达这个规则,那么所有触发都会恢复,回到起点。
提示:你目前不能以大于59小时的时间来创建这种规则,所以如果你设置一个平均率为一天,那么你的触发率必须小于3。
你也可以将此模块用于避免使用快速响应速率的各类拒绝服务攻击(DoS,Denial of Server)。
(译者:以下是较著名的攻击)
Syn-flood protection:
# iptables -A FORWARD -p tcp --syn -m limit --limit 1/s -j ACCEPT
Furtive port scanner:
# iptables -A FORWARD -p tcp --tcp-flags SYN,ACK,FIN,RST RST -m limit --limit 1/s -j ACCEPT
Ping of death:
# iptables -A FORWARD -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT
这个模块工作原理类似于“节流阀”,以下是图示。
rate (pkt/s)
^ .---.
| / DoS
| /
Edge of DoS -|.....:................................ DoS的边界 =
= (limit * | /:
limit-burst) | / : .-.
| / : /
| / : /
End of DoS -|/....:..............:.../.........../. DoS结束
= limit | : :`-' `--'
-------------+-----+--------------+------------------> time (s)
LOGIC => Match | Didn't Match | Match
我们匹配由五个包触发的每秒一个包,不过每秒钟第四个包才开始进入(这个规则),进行三秒钟,然后重新开始。
<--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
你可以看见,前五个包是允许超过一个包/每秒(这个速率)的,然后就开始限制。 如果有一个暂停,那么另一个触发也是允许的,但不能超过规则设置的最大速率。
owner
--uid-owner userid
根据给出的有效的(数值)user id来匹配包的创建进程。
--gid-owner groupid
根据给出的有效的(数值)group id 来匹配包的创建进程。
--pid-owner processid
根据给出的process id 来匹配包的创建进程。
--sid-owner sessionid
根据给出的 session group 来匹配包的创建进程。
unclean
这是试验性模块,必须明确指定'-m unclean'或者'--match unclean'。 它对包进行各种随机判断。此模块还未通过审核,所以不要用在安全设施上。 (可能造成更糟糕的结果,它自己可能还有Bug)。没有提供选项。
7.3.6.5 状态匹配
最有用的匹配标准是'state'扩展。它负责解释'ip_conntrack'模块的connection-tracking分析。 这是推荐使用的(好东东)。
通过指定'-m state'来允许附加的'--state'选项,匹配用逗号分割的状态列表('!'标志表明不符合那些状态(的状态))。
NEW
由新连接创建的包
ESTABLISHED
属于已存在连接的包(也就是说,响应的包)
RELATED
和一个已存在连接有关,但不是它的一部分的包。如ICMP错误,或者(已加载FTP模块)一个建立FTP数据连接的包。
INVALID
由于以下原因而不能被识别的包:包括内存不足和不是相应当前任何连接的ICMP错误。通常这些包会被丢弃。
这个强大的匹配扩展的一个例子:
# iptables -A FORWARD -i ppp0 -m state ! --state NEW -j DROP
7.4 目标规格
现在,我们知道了如何对包进行测试,但是我们还需要告诉那些匹配的包应该如何做。这被称作规则的目标。
有两个很简单的内建目标:DROP和ACCEPT。我们已经看过了。如果包匹配的规则,其目标是这二者中的一个,那么不再考虑更多的规则了:包的命运已经决定。
除此之外有两种目标:扩展的和用户定义的链。
7.4.1 用户定义链
iptables一个强大的特点是由ipchains继承来的可以让用户创建新的链,附加在三个内建的链上(INPUT、FORWARD和OUTPUT)。按照惯例,用户定义链使用小写以区分他们。(我们会在“Operations on an Entire Chains”中描述如何创建新的用户定义链)。
当包匹配的链的目标是一个用户定义链时,包就转移到用户定义链中的规则。如果 没有决定包的命运,那么包在(用户定义链)中的移动就结束了,并回到当前链的下一个规则。
搞搞ASCII艺术吧。考虑两个(笨蛋)链:INPUT(内建的)和test(用户定义的)。
`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 |
----------------------------
考虑一个由192.168.1.1到1.2.3.4的TCP包。它进入INPUT链,由Rule1检查 - 不匹配。 Rule2匹配,那么它的目标就是test,所以下一个检查由test开始。test中的第一个规则 Rule1是匹配的,但是没有指定目标,所以由第二个规则Rule2检查。结果是不匹配,而我们 到达了链的尾部。于是回到INPUT链,因为刚刚被Rule2检查,所以现在由Rule3来检查,仍然 不匹配。
所以这个包的路线是:
v __________________________
`INPUT' | / `test' v
------------------------|--/ -----------------------|----
| Rule1 | /| | Rule1 | |
|-----------------------|/-| |----------------------|---|
| Rule2 / | | Rule2 | |
|--------------------------| -----------------------v----
| Rule3 /--+___________________________/
------------------------|---
v
用户定义链可以跳转到另一个用户定义链(不过不能循环:如果发现循环,包就会被丢弃)。
7.4.2 iptables扩展:新目标
其他类型的扩展是目标。目标扩展由内核模块组成,而且iptables的一个可选扩展提供了新的命令行选项。有几个扩展是包含在默认netfilter发布中的。
LOG
--log-level
跟一个级别名称或数字。合适的名字是(忽略大小写)'debug'、'info'、'notice'、'warning'、'err'、'crit'、'alert'和'emerg',相当于数字7到0。请参考syslog.conf的手册获取这些级别的说明。默认是'warning'。
--log-prefix
跟一个最多29个字符的字符串,它被写入到log信息的开始处,这样可以区别出来。
这个模块最有用的就是跟在limit match后面,这样你就不会被你的log淹没了。
REJECT
此模块和'DROP'效果一样,除了会发送一个'port unreachable'的ICMP错误报文。注意如果属于以下情况,ICMP错误报文不会发送:
o 包一开始就是ICMP错误报文,或者是未知的ICMP类型。
o 包被作为无头的分片过滤了。
o 我们已经向那里发送了太多的ICMP错误报文(参见/proc/sys/net/ipv4/icmp ratelimit)。
7.4.3 特殊的内建目标
有两个特殊的内建目标:RETURN和QUEUE。
RETURN如同到达这个链的尾部:如果是内建的链的规则,那么这个链的默认规则将被执行。如果是用户定义链,当跳至这个链中的这条规则(包含RETURN)时,回到前面的链继续匹配。
QUEUE是一个特别的目标,会为用户空间进程队列这个包。要这样使用,需要两个部件:
o 一个"queue handler",处理用户空间与内核之间的机制。
o 和一个用户空间用来接收的应用程序,可能是操作,以及对包进行裁决。
IPv4 iptables的标准queue handler是 ip_queue 模块,跟随内核发布并标记为实验中。
下面是一个如何用iptables为用户空间进程队列包的快速例子:
# modprobe iptable_filter
# modprobe ip_queue
# iptables -A OUTPUT -p icmp -j QUEUE
在这个例子中,本地生成的送出ICMP包(如由ping产生)到达ip_queue模块,然后包被试图送往用户空间应用。如果没有用户空间应用在(那儿)等着,包就被丢弃了。
要写一个用户空间应用,需要libipq API。和iptables一起发布。在CVS的testsuite tools(如redirect.c)中可以找到相关例子。
可以通过这里检查ip_queue的状态:
/proc/net/ip_queue
队列的最大长度(也就是不包含返回包的送往用户空间包的数量)可以通过这里控制:
/proc/sys/net/ipv4/ip_queue_maxlen
默认队列长度是1024。一旦达到这个长度,新的包就会被丢弃,直到队列长度小于这个值。好的协议如TCP,会对丢弃的包作出拥挤的解释,而且在队列满了后会很理想的将它挡回。无论如何,如果默认值太小的话,最好是多实验以决定队列的最大长度。
7.5 对整个链进行操作
iptables一个非常有用的特性是可以将链中相关的规则成组。你可以随意给链取名,不过我建议使用小写字母以避免与内建的链和目标产生冲突。链的名称最长为31个字母。
7.5.1 创建新链
让我们创建一个新链。因为我是个充满想象的家伙,我叫它test。使用'-N'或'--new-chain'选项:
# iptables -N test
如此简单,现在你可以像上面说的那样放入规则了。
7.5.2 删除链
删除一个链同样简单,使用 '-X'或'--delete-chain'选项。为什么是'-X'?嗯,因为所有合适的字母都已经被使用了。
# iptables -X test
有几个删除链的限制:他们必须是空的(见下面的"Flushing a Chain")而且他们不能是任何规则的目标。你也不能删除任何一个内建的链。
如果你不指定链名的话,所有可以被删除的用户定义链都将被删除。
7.5.3 清空一个链
这是清除一个链中所有规则的简单方法,使用'-F' 或 '--flush'命令。
# iptables -F FORWARD
如果不指定链的话,所有链都将被清空。
7.5.4 对链进行列表
用'-L'或'--list'命令,你可以列出一个链中的所有规则。
用户定义链中的'refcnt'是有多少链的规则指向了它。这个值必须为0,然后才可以删除这个链。
如果链名被忽略,所有链都将被列出,即便是空的。
'-L'可以有三个选项。'-n'(数字)选项对于阻止iptables试图查找IP地址时非常有用,因为(如果你像大多数人一样使用DNS)如果你的DNS设置不太合适的话,可能会造成长时间的停顿,或者你滤掉了DNS请求。它还会让TCP或UDP端口以数字显示。
'-v'选项显示所有规则的细节,包括饱和字节计数器,TOS比较,以及接口。否则这些值是被忽略的。
注意,报和字节计数器可以分别使用'K'、'M'或者'G'来代替1000、1,000,000 和1,000,000,000。使用'-x'(扩展数字)标志来打印整个值,不管它有多大。
7.5.5 重置(清零)计数器
可以重置计数器非常有用。可以用'-Z'或'--zero'来完成。
考虑下面的:
# iptables -L FORWARD
# iptables -Z FORWARD
在上述例子中,有些包在'-L'和'-Z'命令之间通过。因此,你可以把'-L'和'-Z'一起使用,读取时就清空计数器。
7.5.6 设置原则(默认规则)
我们在前面讨论包是如何通过链的时候,已经解释了当包到达内建链的尾部时会发生什么。这时,链的原则就决定包的命运。只有内建的链(INPUT、OUTPUT和FORWARD)有原则,因为如果包到达用户定义链的尾部会返回到前面的链。
原则可以是ACCEPT或DROP,例如:
# iptables -P FORWARD DROP
8. 使用ipchains和ipfwadm
netflter发布中有ipchains.o和ipfwadm.o模块。把其中一个加载到你的内核(注意:他们和ip_tables.o不兼容)。然后你就可以像以前那样使用ipchains和ipfwadm了。
这在一段时间内仍然被支持。我认为合理的计算方式是 2*(替代发布 - 初始的稳定版本),超过了这个时间,就应当使用替代的稳定版本了。这意味着在Linux 2.6或2.8中对它们的支持很可能被放弃。
9. NAT和包过滤的混合使用
想要做网络地址转换(参见NAT HowTo)和包过滤的已很常见。好消息是他们可以混合起来使用的,而且工作得非常好。
你可以完全忽略你的NAT,来定义你的包过滤。包过滤看见的包的源及目标是“真正”的源和目标。例如,如果你将任何发往1.2.3.4 80端口的包DNAT到10.1.1.1的8080端口。包过滤器看见的是包发往10.1.1.1的8080端口(真正的目的地),而非1.2.3.4 的80端口。同样,你可以忽略伪装:看到的是包的真实外部IP地址(如10.1.1.1),而响应的则返回到那里。
你可以使用'state'匹配扩展,使包过滤器不需要做任何额外的工作,因为无论如何,NAT都会要求连接跟踪。扩展NAT HowTo中简单的伪装例子,以禁止任何来自ppp0接口的新的连接,你可以这样:
#对送至ppp0的包进行伪装
iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE
# 禁止由ppp0进入的新的或不合适的包
iptables -A INPUT -i ppp0 -m state --state NEW,INVALID -j DROP
iptables -A FORWARD -i ppp0 -m state --state NEW,INVALID -j DROP
# 开启IP转发
echo 1 > /proc/sys/net/ipv4/ip_forward
10. iptables和ipchains之间的差别
o 首先,内建链的名称从小写改成了大写,因为现在的INPUT和OUTPUT链只获取指向本地和本地生成的包。他们用来检查所有进入和发送的包。
o '-i'标志现在表示进入接口的意思,而且只适用于INPUT和FORWORD链。FORWORD或OUTPUT链中的规则应该将'-i'改为'-o'。
o TCP和UDP端口现在必须用--source-port或--sport(或者--destination-port/--dport)拼写,而且必须放在'-p tcp'或'-p udp'选项之后,因为TCP或UDP扩展是分别加载的。
o TCP -y 标志现在是 --syn,而且必须在'-p tcp'之后。
o DENY目标现在是DROP.
o 对单个链,可以在列出其工作同时清零。
o 清空内建链同时清除了原则计数器。
o 列出链给出的是一个计数器的微型的快照。
o REJECT和LOG现在是扩展目标,意思是他们是独立的内核模块。
o 链的名称最多可以是31个字符。
o MASQ现在是MASQUERADE而且使用不同的语法。REDRIRECT,在保留相同的名字时,也经历了语法的改变。参见NAT-HOWTO以获取配置它们的更多信息。
o -o选项不再用于将包传递给用户空间设备了(见上面的-i)。现在通过 QUEUE目标传递到用户空间。
o 很可能还有一些我也忘了。
11. 对制定包过滤器的建议
在计算机安全领域中,最明智的办法是阻挡所有东西,然后对需要的开启。这通常称为“凡是没有明确允许的都是禁止的”。我建议这样做如果安全是你最关心的。
不要运行任何你不需要的服务,即使你认为你已经阻碍了对它们的访问。
如果你创建专用防火墙,开始时不运行任何东西,并阻止所有包,然后添加服务并让需要的包通过。
我强调安全:结合tcp-wrappers(对于包过滤器本身的连接),代理(通过包过滤器的连接),路由验证和包过滤。路由验证是如果包来自未预期的接口那么将被删除:例如,如果你的内部网络地址是10.1.1.0/24,而一个包的源地址是你的外部接口,那么它将被丢弃。对一个接口如ppp0来说可以这样:
# echo 1 > /proc/sys/net/ipv4/conf/ppp0/rp_filter
或者对所有已有的或将有的接口:
# for f in /proc/sys/net/ipv4/conf/*/rp_filter; do
# echo 1 > $f
# done
Debian在可能的范围了将这些设为默认。如果你使用非对称路由(如你期望包来自一个其他的方向),你可能需要在这些接口上禁止这一过滤。
记录对于当工作不正常时设置防火墙非常有用,但是在一个作为产品的防火墙上,总是应当将它与'limit'匹配结合,以防止有人充斥你的记录。
我极力推荐对安全系统使用连接追踪:它虽然会造成负担,因为所有连接都被追踪。但是对于控制对你的网络的访问非常有用。如果你的内核没有自动加载而且没有内建,你需要加载'ip_conntrack.o'这个模块。如果想要精确追踪复杂的协议,你需要加载合适的相关模块(如'ip_conntrack_ftp.o')。
# 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
建造一个好的防火墙超越了这个HOWTO的范围,不过我的建议是“一切从严”。请参见Security HOWTO获取更多信息,来测试和探索你的服务器。
all pages ended here.