注意: 这一节的内容正在撰写中。 其内容可能不总是十分准确。
IPFILTER 的作者是 Darren Reed。 IPFILTER 是独立于操作系统的: 它是一个开放源代码的应用, 并且已经被移植到了 FreeBSD、 NetBSD、 OpenBSD、 SunOS、 HP/UX, 以及 Solaris 操作系统上。 IPFILTER 的支持和维护都相当活跃, 并且有规律地发布更新版本。
IPFILTER 基于内核模式的防火墙和 NAT 机制, 这些机制可以通过用户模式运行的接口程序中进行控制。 防火墙规则可以使用 ipf(8) 工具来动态地设置和删除。 NAT 规则可以通过 ipnat(1) 工具来维护。 ipfstat(8) 工具则可以用来显示 IPFILTER 内核部分的统计数据。 最后, 使用 ipmon(8) 程序可以把 IPFILTER 的动作记录到系统日志文件中。
IPF 最初是使用一组 “以最后匹配的规则为准” 的策略来实现的, 这种方式只能支持无状态的规则。 随着时代的进步, IPF 被逐渐增强, 并加入了 “quick” 选项, 以及支持状态的 “keep state” 选项, 这使得规则处理逻辑变得更富有现代气息。 IPF 的官方文档介绍了传统的规则编写方法和文件处理逻辑。 新增的功能只是作为一些附加的选项出现, 如果能完全理解这些功能, 则对于建立更安全的防火墙就很有好处。
这一节中主要是针对 “quick” 选项, 以及支持状态的 “keep state” 选项的介绍。 这是包容式防火墙规则集最基本的编写要素。
包容式防火墙只允许与规则匹配的包通过。 这样, 您就既能够控制来自防火墙后面的机器请求 Internet 公网上的那些服务, 同时也可以控制来自 Internet 的请求能够访问内部网上的哪些服务。 所有其它的访问请求都会被阻止, 并记录下来。 包容式防火墙一般而言要远比排斥式的要安全, 而且也只需要定义允许哪些访问通过。
要获得关于传统规则处理方式的详细信息, 请参考: http://www.obfuscation.org/ipf/ipf-howto.html#TOC_1 以及 http://coombs.anu.edu.au/~avalon/ip-filter.html。
IPF FAQ 可以在 http://www.phildev.net/ipf/index.html 找到。
除此之外, 您还可以在 http://marc.theaimsgroup.com/?l=ipfilter 找到开放源代码的 IPFilter 的邮件列表存档, 并进行搜索。
IPF 作为 FreeBSD 基本安装的一部分, 以一个独立的内核模块的形式提供。 如果在 rc.conf 中配置了 ipfilter_enable="YES", 系统就会自动地动态加载 IPF 内核模块。 这个内核模块在创建时启用了日志支持, 并加入了 default pass all 选项。 如果只是需要把默认的规则设置为 block all 的话, 并不需要把 IPF 编译到内核中。 可以简单地通过把这条规则加入自己的规则集来达到同样的目的。
下面这些 FreeBSD 内核编译选项并不是启用 IPF 所必需的。 这里只是作为背景知识来加以阐述。 如果将 IPF 编入了内核, 则对应的内核模块将不被使用。
关于 IPF 选项语句的内核编译配置的例子, 可以在内核源代码中的 /usr/src/sys/conf/NOTES 找到。 此处列举如下:
options IPFILTER options IPFILTER_LOG options IPFILTER_DEFAULT_BLOCK
options IPFILTER 用于启用 “IPFILTER” 防火墙的支持。
options IPFILTER_LOG 用于启用 IPF 的日志支持, 所有匹配了包含 log 的规则的包, 都会被记录到 ipl 这个包记录伪──设备中。
options IPFILTER_DEFAULT_BLOCK 将改变防火墙的默认动作, 进而, 所有不匹配防火墙的 pass 规则的包都会被阻止。
这些选项只有在您重新编译并安装内核之后才会生效。
要在启动时激活 IPF, 您需要在 /etc/rc.conf 中增加下面的设置:
ipfilter_enable="YES" # 启动 ipf 防火墙 ipfilter_rules="/etc/ipf.rules" # 将被加载的规则定义, 这是一个文本文件 ipmon_enable="YES" # 启动 IP 监视日志 ipmon_flags="-Ds" # D = 作为服务程序启动 # s = 使用 syslog 记录 # v = 记录 tcp 窗口大小、 ack 和顺序号(seq) # n = 将 IP 和端口映射为名字
如果您的 LAN 在防火墙后面, 并且使用了保留的私有 IP 地址范围, 那就需要增加下面的一些选项来启用 NAT 功能:
gateway_enable="YES" # 启用作为 LAN 网关的功能 ipnat_enable="YES" # 启动 ipnat 功能 ipnat_rules="/etc/ipnat.rules" # 用于 ipnat 的规则定义文件
ipf 命令可以用来加载您自己的规则文件。 一般情况下, 您可以建立一个包括您自定义的规则的文件, 并使用这个命令来替换掉正在运行的防火墙中的内部规则:
# ipf -Fa -f /etc/ipf.rules
-Fa
表示清除所有的内部规则表。
-f
用于指定将要被读取的规则定义文件。
这个功能使得您能够修改自定义的规则文件, 通过运行上面的 IPF 命令, 可以将正在运行的防火墙刷新为使用全新的规则集, 而不需要重新启动系统。 这对于测试新的规则来说就很方便, 因为您可以任意执行上面的命令。
请参考 ipf(8) 联机手册以了解这个命令提供的其它选项。
ipf(8) 命令假定规则文件是一个标准的文本文件。 它不能处理使用符号代换的脚本。
也确实有办法利用脚本的非常强大的符号替换能力来构建 IPF 规则。 要了解进一步的细节, 请参考 第 26.5.9 节。
默认情况下, ipfstat(8) 会获取并显示所有的累积统计, 这些统计是防火墙启动以来用户定义的规则匹配的出入流量, 您可以通过使用 ipf -Z 命令来将这些计数器清零。
请参见 ipfstat(8) 联机手册以了解进一步的细节。
默认的 ipfstat(8) 命令输出类似于下面的样子:
input packets: blocked 99286 passed 1255609 nomatch 14686 counted 0 output packets: blocked 4200 passed 1284345 nomatch 14687 counted 0 input packets logged: blocked 99286 passed 0 output packets logged: blocked 0 passed 0 packets logged: input 0 output 0 log failures: input 3898 output 0 fragment state(in): kept 0 lost 0 fragment state(out): kept 0 lost 0 packet state(in): kept 169364 lost 0 packet state(out): kept 431395 lost 0 ICMP replies: 0 TCP RSTs sent: 0 Result cache hits(in): 1215208 (out): 1098963 IN Pullups succeeded: 2 failed: 0 OUT Pullups succeeded: 0 failed: 0 Fastroute successes: 0 failures: 0 TCP cksum fails(in): 0 (out): 0 Packet log flags set: (0)
如果使用了 -i
(进入流量) 或者 -o
(输出流量),
它就只获取并显示内核中所安装的对应过滤器规则的统计数据。
ipfstat -in 以规则号的形式显示进入的内部规则表。
ipfstat -on 以规则号的形式显示流出的内部规则表。
输出和下面的类似:
@1 pass out on xl0 from any to any @2 block out on dc0 from any to any @3 pass out quick on dc0 proto tcp/udp from any to any keep state
ipfstat -ih 显示内部规则表中的进入流量, 每一个匹配规则前面会同时显示匹配的次数。
ipfstat -oh 显示内部规则表中的流出流量, 每一个匹配规则前面会同时显示匹配的次数。
输出和下面的类似:
2451423 pass out on xl0 from any to any 354727 block out on dc0 from any to any 430918 pass out quick on dc0 proto tcp/udp from any to any keep state
ipfstat 命令的一个重要的功能可以通过指定 -t
参数来使用, 它会以类似 top(1) 的显示 FreeBSD
正运行的进程表的方式来显示统计数据。 当您的防火墙正在受到攻击的时候,
这个功能让您得以识别、 试验, 并查看攻击的数据包。
这个选项提还提供了实时选择希望监视的目的或源 IP、 端口或协议的能力。 请参见 ipfstat(8)
联机手册以了解详细信息。
为了使 ipmon 能够正确工作, 必须打开 IPFILTER_LOG
这个内核选项。 这个命令提供了两种不同的使用模式。 内建模式是默认的模式, 如果您不指定
-D
参数, 就会采用这种模式。
服务模式是持续地通过系统日志来记录的工作模式, 这样,
您就可以通过查看日志来了解过去曾经发生过的事情。 这种模式是 FreeBSD 和 IPFILTER
配合工作的模式。 由于在 FreeBSD 中提供了一个内建的系统日志自动轮转功能, 因此, 使用
syslogd 比默认的将日志信息记录到一个普通文件要好。 在默认的 rc.conf 文件中, 您会看到一个 ipmon_flags 语句, 指定了 -Ds
标志:
ipmon_flags="-Ds" # D = 作为服务程序启动 # s = 使用 syslog 记录 # v = 记录 tcp 窗口大小、 ack 和顺序号(seq) # n = 将 IP 和端口映射为名字
记录日志的好处是很明显的。 它提供了在事后重新审查相关信息, 例如哪些包被丢弃, 以及这些包的来源地址等等。 这将为查找攻击者提供非常有用的第一手资料。
即使启用了日志机制, IPF 仍然不会对其规则进行任何日志记录工作。 防火墙管理员可以决定规则集中的哪些应记录日志, 并在这些规则上加入 log 关键字。 一般来说, 只应记录拒绝性的规则。
作为惯例, 通常会有一条默认的、拒绝所有网络流量的规则, 并指定 log 关键字, 作为您的规则集的最后一条。 这样, 您就能够看到所有没有匹配任何规则的数据包。
Syslogd 使用特殊的方法对日志数据进行分类。 它使用称为
“facility” 和 “level” 的组。 以 -Ds
模式运行的 IPMON 采用
security (在 4.X 中是local0) 作为
“facility” 名。 所有由 IPMON 记录的数据都会进入 security (在
4.X 中是 local0)。 如果需要, 可以用下列 levels
来进一步区分数据:
LOG_INFO - 使用 "log" 关键字指定的通过或阻止动作 LOG_NOTICE - 同时记录通过的那些数据包 LOG_WARNING - 同时记录阻止的数据包 LOG_ERR - 进一步记录含不完整的包头的数据包
要设置 IPFILTER 来将所有的数据记录到 /var/log/ipfilter.log, 需要首先建立这个文件。 下面的命令可以完成这个工作:
# touch /var/log/ipfilter.log
syslog 功能可以通过在 /etc/syslog.conf 文件中的语句来定义。 syslog.conf 提供了相当多的用以控制 syslog 如何处理类似 IPF 这样的用用程序所产生的系统消息的方法。
对 FreeBSD 5.X 和更新版本, 将下列语句加到 /etc/syslog.conf:
security.* /var/log/ipfilter.log
或将下列语句加入 /etc/syslog.conf。
这里的 security.* 表示把所有的相关日志信息写到指定的文件中。
要让 /etc/syslog.conf 中的修改立即生效, 您可以重新启动计算机, 或者通过执行 /etc/rc.d/syslogd reload 来让它重新读取 /etc/syslog.conf。
不要忘了修改 /etc/newsyslog.conf 来让您刚创建的日志进行轮转。
由 ipmon 生成的消息由空格分隔的数据字段组成。 所有的消息都包含的字段是:
接到数据包的日期。
接到数据包的时间。 其格式为 HH:MM:SS.F, 分别是小时、 分钟、 秒, 以及分秒 (这个数字可能有许多位)。
处理数据包的网络接口名字, 例如 dc0。
组和规则的编号, 例如 @0:17。
可以通过 ipfstat -in 来查看这些信息。
动作: p 表示通过, b 表示阻止, S 表示包头不全, n 表示没有匹配任何规则, L 表示 log 规则。 显示这些标志的顺序是: S, p, b, n, L。 大写的 P 或 B 表示记录包的原因是某个全局的日志配置, 而不是某个特定的规则。
地址。 这实际上包括三部分: 源地址和端口 (以逗号分开), 一个 -> 符号, 以及目的地址和端口。 209.53.17.22,80 -> 198.73.220.17,1722.
PR, 后跟协议名称或编号, 例如, PR tcp。
len, 后跟包头的长度, 以及包的总长度, 例如 len 20 40。
对于 TCP 包, 则还会包括一个附加的字段, 由一个连字号开始, 之后是表示所设置的标志的一个字母。 请参见 ipmon(8) 联机手册, 以了解这些字母所对应的标志。
对于 ICMP 包, 则在最后会有两个字段。 前一个总是 “ICMP”, 而后一个则是 ICMP 消息和子消息的类型, 中间以斜线分靠, 例如 ICMP 3/3 表示端口不可达消息。
一些有经验的 IPF 会创建包含规则的文件, 并把它编写成能够与符号替换脚本兼容的方式。 这样做最大的好处是, 它能够让您只修改符号名字所代表的值, 而在脚本执行时直接替换掉所有的名符。 作为脚本, 您可以使用符号替换来把那些经常使用的值直接用于多个规则。 下面我们将给出一个例子。
这个脚本所使用的语法与 sh、 csh, 以及 tcsh 脚本。
符号替换的前缀字段是美元符号: $。
符号字段不使用 $ 前缀。
希望替换符号字段的值, 必须使用双引号 (") 括起来。
您的规则文件的开头类似这样:
############# IPF 规则脚本的开头 ######################## oif="dc0" # 外网接口的名字 odns="192.0.2.11" # ISP 的 DNS 服务器 IP 地址 myip="192.0.2.7" # 来自 ISP 的静态 IP 地址 ks="keep state" fks="flags S keep state" # 可以使用这个脚本来建立 /etc/ipf.rules 文件, # 也可以 "直接地" 运行它。 # # 请删除两个注释号之一。 # # 1) 保留下面一行, 则创建 /etc/ipf.rules: #cat > /etc/ipf.rules << EOF # # 2) 保留下面一行, 则 "直接地" 运行脚本: /sbin/ipf -Fa -f - << EOF # 允许发出到我的 ISP 的域名服务器的访问 pass out quick on $oif proto tcp from any to $odns port = 53 $fks pass out quick on $oif proto udp from any to $odns port = 53 $ks # 允许发出未加密的 www 访问请求 pass out quick on $oif proto tcp from $myip to any port = 80 $fks # 允许发出使用 TLS SSL 加密的 https www 访问请求 pass out quick on $oif proto tcp from $myip to any port = 443 $fks EOF ################## IPF 规则脚本的结束 ########################
这就是所需的全部内容。 这个规则本身并不重要, 它们主要是用于体现如何使用符号代换字段, 以及如何完成值的替换。 如果上面的例子的名字是 /etc/ipf.rules.script, 就可以通过输入下面的命令来重新加载规则:
# sh /etc/ipf.rules.script
在规则文件中嵌入符号有一个问题: IPF 无法识别符号替换, 因此它不能直接地读取这样的脚本。
这个脚本可以使用下面两种方法之一来使用:
去掉 cat 之前的注释, 并注释掉 /sbin/ipf 开头的那一行。 像其他配置一样, 将 ipfilter_enable="YES" 放到 /etc/rc.conf 文件中, 并在此后立刻执行脚本, 以创建或更新 /etc/ipf.rules。
通过把 ipfilter_enable="NO" (这是默认值) 加到 /etc/rc.conf 中, 来禁止系统启动脚本开启 IPFILTER。
在 /usr/local/etc/rc.d/ 启动目录中增加一个类似下面的脚本。 应该给它起一个显而易见的名字, 例如 ipf.loadrules.sh。 请注意, .sh 扩展名是必需的。
#!/bin/sh sh /etc/ipf.rules.script
脚本文件必须设置为属于 root, 并且属主可读、 可写、 可执行。
# chmod 700 /usr/local/etc/rc.d/ipf.loadrules.sh
这样, 在系统启动时, 就会自动加载您的 IPF 规则了。
规则集是指一组编写好的依据包的值决策允许通过或阻止 ipf 规则。 包的双向交换组成了一个会话交互。 防火墙规则集对同一个包会进行两次处理, 第一次是它从公网的 Internet 主机到达的时候, 第二次是它离开并返回初始的 Internet 公网主机的时候。 每一个 TCP/IP 服务 (例如 telnet, www, 邮件等等) 是由协议预先定义的源或目的 IP 地址, 以及源或目的端口。 这是最基本的一些可以为防火墙规则所利用的, 判别是否允许服务通过的标准。
IPF 最初被写成使用一组称作 “以最后匹配的规则为准” 的处理逻辑, 且只能处理无状态的规则。 随着时代的发展, IPF 进行了改进, 并提供了 “quick” 选项, 以及一个有状态的 “keep state” 选项。 后者使处理逻辑迅速地跟上了时代的步伐。
这一节中提供的一些指导, 是基于使用包含 “quick” 选项和有状态的 “keep state” 选项来进行阐述的。 这些是编写包容式防火墙规则集的基本要素。
包容式防火墙只允许与规则匹配的服务通过。 这样, 您就既能够控制来自防火墙后面的机器请求 Internet 公网上的那些服务, 同时也可以控制来自 Internet 的请求能够访问内部网上的哪些服务。 所有其它的访问请求都会被阻止, 并记录下来。 包容式防火墙一般而言要远比排斥式的要安全, 而且也只需要定义允许哪些访问通过。
警告: 当对防火墙规则进行操作时, 应该 谨慎行事。 某些配置可能会 将您反锁在 服务器外面。 安全起见, 您可以考虑在第一次进行防火墙配置时在本地控制台上, 而不是远程, 例如通过 ssh 来进行。
这里给出的规则语法已经简化到只处理那些新式的带状态规则, 并且都是 “第一个匹配的规则获胜” 逻辑的。 要了解完整的传统规则语法描述, 请参见 ipf(8) 联机手册。
以 # 字符开头的内容会被认为是注释。 这些注释可以出现在一行规则的末尾, 或者独占一行。 空行会被忽略。
规则由关键字组成。 这些关键字必须以一定的顺序, 从左到右出现在一行上。 接下来的文字中关键字将使用粗体表示。 某些关键字可能提供了子选项, 这些子选项本身可能也是关键字, 而且可能会提供更多的子选项。 下面的文字中, 每种语法都使用粗体的小节标题呈现, 并介绍了其上下文。
ACTION IN-OUT OPTIONS SELECTION STATEFUL PROTO SRC_ADDR,DST_ADDR OBJECT PORT_NUM TCP_FLAG STATEFUL
ACTION = block | pass
IN-OUT = in | out
OPTIONS = log | quick | on 网络接口的名字
SELECTION = proto 协议名称 | 源/目的 IP | port = 端口号 | flags 标志值
PROTO = tcp/udp | udp | tcp | icmp
SRC_ADD,DST_ADDR = all | from 对象 to 对象
OBJECT = IP地址 | any
PORT_NUM = port 端口号
TCP_FLAG = S
STATEFUL = keep state
动作对表示匹配规则的包应采取什么动作。 每一个规则 必须 包含一个动作。 可以使用下面两种动作之一:
block 表示如果规则与包匹配, 则丢弃包。
pass 表示如果规则与包匹配, 则允许包通过防火墙。
每个过滤器规则都必须明确地指定是流入还是流出的规则。 下一个关键字必须要么是 in, 要么是 out, 否则将无法通过语法检查。
in 表示规则应被应用于刚刚从 Internet 公网上收到的数据包。
out 表示规则应被应用于即将发出到 Internet 的数据包。
注意: 这些选项必须按下面指定的顺序出现。
log 表示包头应被写入到 ipl 日志 (如前面 LOGGING 小节所介绍的那样), 如果它与规则匹配的话。
quick 表示如果给出的参数与包匹配, 则以这个规则为准, 这使得能够 "短路" 掉后面的规则。 这个选项对于使用新式的处理逻辑是必需的。
on 表示将网络接口的名称作为筛选参数的一部分。 接口的名字会在 ifconfig(8) 的输出中显示。 使用这个选项, 则规则只会应用到某一个网络接口上的出入数据包上。 要配置新式的处理逻辑, 必须使用这个选项。
当记录包时, 包的头会被写入到 IPL 包日志伪设备中。 紧跟 log 关键字, 可以使用下面几个修饰符 (按照下列顺序):
body 表示应同时记录包的前 128 字节的内容。
first 如果 log 关键字和 “keep state” 选项同时使用, 则这个选项只在第一个包上触发, 这样就不用记录每一个 “keep state” 包信息了。
这一节所介绍的关键字可以用于所检察的包的属性。 有一个关键字主题, 以及一组子选项关键字, 您必须从他们中选择一个。 以下是一些通用的属性, 它们必须按下面的顺序使用:
proto 是一个主题关键字, 它必须与某个相关的子选项关键字配合使用。 这个值的作用是匹配某个特定的协议。 要使用新式的规则处理逻辑, 就必须使用这个选项。
tcp/udp | udp | tcp | icmp 或其他在 /etc/protocols 中定义的协议。 特殊的协议关键字 tcp/udp 可以用于匹配 TCP 或 UDP 包, 引入这个关键字的作用是是避免大量的重复规则的麻烦。
使用 all 关键词, 基本上相当于 “from any to any” 在没有配合其他关键字的情形。
from src to dst: from 和 to 关键字主要是用来匹配 IP 地址。 所有的规则都必须同时给出源和目的两个参数。 any 是一个可以用于匹配任意 IP 地址的特殊关键字。 例如, 您可以使用 “from any to any” 或 “from 0.0.0.0/0 to any” 或 “from any to 0.0.0.0/0” 或 “from 0.0.0.0 to any” 以及 “from any to 0.0.0.0”。
IP 地址可以按句点分隔的 IP 地址/掩码长度 的方式来指定, 也可以只指定一个句点分隔的 IP 地址。
如果无法使用子网掩码来表示 IP 的话, 表达地址就会很麻烦。 请参见下面的网页了解如何撰写掩码长度: http://jodies.de/ipcalc。
如果为源或目的指定了匹配端口, 规则就只能应用于 TCP 和 UDP 包了。 当编写端口比较规则时, 可以指定 /etc/services 中所定义的名字, 也可以直接用端口号来指定。 如果端口号出现在源对象一侧, 则被认为是源端口号; 反之, 则被认为是目的端口号。 要使用新式的规则处理逻辑, 就必须与 to 对象配合使用这个选项。 使用的例子: “from any to any port = 80”。
端口的比较是以数字的形式进行的, 可以使用比较算符来指定, 也可以指定一个范围。
port "=" | "!=" | "<" | ">" | "<=" | ">=" | "eq" | "ne" | "lt" | "gt" | "le" | "ge".
要指定端口范围, 可以使用 "<>" | "><"。
警告: 在源和目的匹配参数之后, 需要使用下面两个参数, 才能够使用新式的规则处理逻辑。
有状态过滤将网络流量当作一种双向的包交换来处理。 如果激活它, keep-state 会动态地为每一个相关的包在双向会话交互过程中产生内部规则。 它能够确认发起者和包的目的地之间的会话是有效的双向包交换过程的一部分。 如果包与这些规则不符, 则将自动地拒绝。
状态保持也使得 ICMP 包能够与 TCP 或 UDP 会话相关。 因此, 如果您在浏览网站时收到允许的状态保持规则匹配的 ICMP 类型 3 代码 4 响应, 则这些响应会被自动地允许进入。 所有 IPF 能够处理的包, 都可以作为某种活跃会话的一部分, 即使它是另一种协议的, 也会被允许进入。
所发生的事情是:
将要通过联入公网的网络接口发出的包, 首先会经过动态状态表的检查。 如果包与会话中预期的下一个包匹配, 则防火墙就会允许包通过, 而会话的交互流信息也会在动态状态表中进行更新, 而其他的包, 则将使用发出规则集来检查。
发到联入 Internet 公网的包, 也会首先经过动态规则表的检查。 如果与会话中预期的下一个包匹配, 则防火墙就允许它通过, 并更新动态状态表。 其他包仍会使用进入规则集进行检查。
当会话结束时, 对应的项会在动态状态表中删除。
有状态过滤使得您能够集中于阻止/允许新的会话。 一旦新会话被允许通过, 则所有后续的包就都被自动地允许通过, 而伪造的包则被自动地拒绝。 如果新的会话被阻止, 则后续的包也都不会被允许通过。 有状态过滤从技术角度而言, 在阻止目前攻击者常用的洪水式攻击来说, 具有更好的抗御能力。
下面的规则集是如何编写非常安全的包容式防火墙规则集的一个范例。 包容式防火墙只让允许的服务通过, 而所有其他的访问都会被默认地拒绝。 所有的防火墙都有至少两个接口对应的默认规则, 从而使防火墙能够正常工作。
所有的类 UNIX® 系统, 包括 FreeBSD 都使用 lo0 和 IP 地址 127.0.0.1 用于操作系统中内部的通讯。 防火墙规则必须允许这些包无阻碍地通过。
接入 Internet 公网的网络接口, 是放置规则并允许将访问请求发到 Internet 以及接收响应的地方。 这有可能是用户模式的 PPP tun0 接口, 如果您的网卡同 DSL 或电缆调制解调器相联的话。
如果有至少一个网卡与防火墙后的内网 LAN 相联, 这些网络接口就应该有一个规则来允许来自这些 LAN 接口的包无阻碍地通过。
一般说来, 规则应被组织为三个主要的小节: 所有允许自由通过的接口规则, 发到公网接口的规则, 以及进入公网接口的规则。
每一个公网接口规则中, 经常会匹配到的规则应该放置在尽可能靠前的位置。 而最后一个规则应该是阻止包通过, 并记录它们。
下面防火墙规则集中, Outbound 部分是一些使用 'pass' 的规则, 这些规则指定了允许访问的公网 Internet 服务, 并且指定了 'quick'、 'on'、 'proto'、 'port', 以及 'keep state' 这些选项。 'proto tcp' 规则还指定了 'flag' 这个选项, 这样会话的第一个包将出发状态机制。
接下来的 Inbound 一节, 则首先阻止所有不希望的数据包。 这样做有两个原因, 其一是被阻止的包可能会被后面的规则允许, 从而并不妨碍获得授权的服务正常工作; 其二是这避免了那些不常见的包由于匹配到最后一条规则而触发日志, 规则集中的最后一条规则是阻止并记录所有的包, 通过这样的记录, 就比较容易找到攻击系统的人, 并为采取法律措施收集证据。
需要注意的另一件事情是, 如果收到了不希望的数据包, 则这些包会被丢弃, 而不是给出什么响应。 这样做的好处是, 攻击者无法了解包是否已经被您的系统收到。 攻击者所能了解到的信息越少, 攻陷您的系统所需要花费的时间也就越长。 我们在这里记录的连入的 'nmap OS 指纹' 探测企图, 一般来说正是攻击者所做的第一件事。
如果您看到了 'log first' 规则的日志, 就应该用 ipfstat -hio 命令来看看那个规则被匹配的次数, 以便了解系统是否正在或曾被攻击。
如果记录的包的端口号并不是您所知道的, 可以在 /etc/services 或 http://www.securitystats.com/tools/portsearch.php 了解端口号通常的用途。
参考下面的网页, 了解木马使用的端口:http://www.simovits.com/trojans/trojans.html。
下面是我在自己的系统中使用的完整的, 非常安全的 '包容式' 防火墙规则集。 直接使用这个规则集不会给您造成问题, 您所要做的只是注释掉那些您不需要的服务。
如果在日志中发现了希望阻止的记录, 只需在 inbound 小节中增加一条阻止规则集可。
您必须将每一个规则中的 dc0 替换为您系统上接入 Internet 的网络接口名称, 例如, 用户环境下的 PPP 应该是 tun0。
在 /etc/ipf.rules 中加入下面的内容:
################################################################# # No restrictions on Inside LAN Interface for private network # Not needed unless you have LAN ################################################################# #pass out quick on xl0 all #pass in quick on xl0 all ################################################################# # No restrictions on Loopback Interface ################################################################# pass in quick on lo0 all pass out quick on lo0 all ################################################################# # Interface facing Public Internet (Outbound Section) # Interrogate session start requests originating from behind the # firewall on the private network # or from this gateway server destine for the public Internet. ################################################################# # Allow out access to my ISP's Domain name server. # xxx must be the IP address of your ISP's DNS. # Dup these lines if your ISP has more than one DNS server # Get the IP addresses from /etc/resolv.conf file pass out quick on dc0 proto tcp from any to xxx port = 53 flags S keep state pass out quick on dc0 proto udp from any to xxx port = 53 keep state # Allow out access to my ISP's DHCP server for cable or DSL networks. # This rule is not needed for 'user ppp' type connection to the # public Internet, so you can delete this whole group. # Use the following rule and check log for IP address. # Then put IP address in commented out rule & delete first rule pass out log quick on dc0 proto udp from any to any port = 67 keep state #pass out quick on dc0 proto udp from any to z.z.z.z port = 67 keep state # Allow out non-secure standard www function pass out quick on dc0 proto tcp from any to any port = 80 flags S keep state # Allow out secure www function https over TLS SSL pass out quick on dc0 proto tcp from any to any port = 443 flags S keep state # Allow out send & get email function pass out quick on dc0 proto tcp from any to any port = 110 flags S keep state pass out quick on dc0 proto tcp from any to any port = 25 flags S keep state # Allow out Time pass out quick on dc0 proto tcp from any to any port = 37 flags S keep state # Allow out nntp news pass out quick on dc0 proto tcp from any to any port = 119 flags S keep state # Allow out gateway & LAN users non-secure FTP ( both passive & active modes) # This function uses the IPNAT built in FTP proxy function coded in # the nat rules file to make this single rule function correctly. # If you want to use the pkg_add command to install application packages # on your gateway system you need this rule. pass out quick on dc0 proto tcp from any to any port = 21 flags S keep state # Allow out secure FTP, Telnet, and SCP # This function is using SSH (secure shell) pass out quick on dc0 proto tcp from any to any port = 22 flags S keep state # Allow out non-secure Telnet pass out quick on dc0 proto tcp from any to any port = 23 flags S keep state # Allow out FBSD CVSUP function pass out quick on dc0 proto tcp from any to any port = 5999 flags S keep state # Allow out ping to public Internet pass out quick on dc0 proto icmp from any to any icmp-type 8 keep state # Allow out whois for LAN PC to public Internet pass out quick on dc0 proto tcp from any to any port = 43 flags S keep state # Block and log only the first occurrence of everything # else that's trying to get out. # This rule enforces the block all by default logic. block out log first quick on dc0 all ################################################################# # Interface facing Public Internet (Inbound Section) # Interrogate packets originating from the public Internet # destine for this gateway server or the private network. ################################################################# # Block all inbound traffic from non-routable or reserved address spaces block in quick on dc0 from 192.168.0.0/16 to any #RFC 1918 private IP block in quick on dc0 from 172.16.0.0/12 to any #RFC 1918 private IP block in quick on dc0 from 10.0.0.0/8 to any #RFC 1918 private IP block in quick on dc0 from 127.0.0.0/8 to any #loopback block in quick on dc0 from 0.0.0.0/8 to any #loopback block in quick on dc0 from 169.254.0.0/16 to any #DHCP auto-config block in quick on dc0 from 192.0.2.0/24 to any #reserved for docs block in quick on dc0 from 204.152.64.0/23 to any #Sun cluster interconnect block in quick on dc0 from 224.0.0.0/3 to any #Class D & E multicast ##### Block a bunch of different nasty things. ############ # That I do not want to see in the log # Block frags block in quick on dc0 all with frags # Block short tcp packets block in quick on dc0 proto tcp all with short # block source routed packets block in quick on dc0 all with opt lsrr block in quick on dc0 all with opt ssrr # Block nmap OS fingerprint attempts # Log first occurrence of these so I can get their IP address block in log first quick on dc0 proto tcp from any to any flags FUP # Block anything with special options block in quick on dc0 all with ipopts # Block public pings block in quick on dc0 proto icmp all icmp-type 8 # Block ident block in quick on dc0 proto tcp from any to any port = 113 # Block all Netbios service. 137=name, 138=datagram, 139=session # Netbios is MS/Windows sharing services. # Block MS/Windows hosts2 name server requests 81 block in log first quick on dc0 proto tcp/udp from any to any port = 137 block in log first quick on dc0 proto tcp/udp from any to any port = 138 block in log first quick on dc0 proto tcp/udp from any to any port = 139 block in log first quick on dc0 proto tcp/udp from any to any port = 81 # Allow traffic in from ISP's DHCP server. This rule must contain # the IP address of your ISP's DHCP server as it's the only # authorized source to send this packet type. Only necessary for # cable or DSL configurations. This rule is not needed for # 'user ppp' type connection to the public Internet. # This is the same IP address you captured and # used in the outbound section. pass in quick on dc0 proto udp from z.z.z.z to any port = 68 keep state # Allow in standard www function because I have apache server pass in quick on dc0 proto tcp from any to any port = 80 flags S keep state # Allow in non-secure Telnet session from public Internet # labeled non-secure because ID/PW passed over public Internet as clear text. # Delete this sample group if you do not have telnet server enabled. #pass in quick on dc0 proto tcp from any to any port = 23 flags S keep state # Allow in secure FTP, Telnet, and SCP from public Internet # This function is using SSH (secure shell) pass in quick on dc0 proto tcp from any to any port = 22 flags S keep state # Block and log only first occurrence of all remaining traffic # coming into the firewall. The logging of only the first # occurrence stops a .denial of service. attack targeted # at filling up your log file space. # This rule enforces the block all by default logic. block in log first quick on dc0 all ################### End of rules file #####################################
NAT 是 网络地址转换(Network Address Translation) 的缩写。 对于那些熟悉 Linux® 的人来说, 这个概念叫做 IP 伪装 (Masquerading); NAT 和 IP 伪装是完全一样的概念。 由 IPF 的 NAT 提供的一项功能是, 将防火墙后的本地局域网 (LAN) 共享一个 ISP 提供的 IP 地址来接入 Internet 公网。
有些人可能会问, 为什么需要这么做。 一般而言, ISP 会为非商业用户提供动态的 IP 地址。 动态地址意味着每次登录到 ISP 都有可能得到不同的 IP 地址, 无论是采用电话拨号登录, 或使用 cable 以及 DSL 调制解调器的方式。 这个 IP 是您与 Internet 公网交互时使用的身份。
现在考虑家中有五台 PC 需要访问 Internet 的情形。 您可能需要向 ISP 为每一台 PC 所使用的独立的 Internet 账号付费, 并且拥有五根电话线。
有了 NAT, 您就只需要一个 ISP 账号, 然后将另外四台 PC 的网卡通过交换机连接起来, 并通过运行 FreeBSD 系统的那台机器作为网关连接出去。 NAT 会自动地将每一台 PC 在内网的 LAN IP 地址, 在离开防火墙时转换为公网的 IP 地址。 此外, 当数据包返回时, 也将进行逆向的转换。
NAT 通常是在没有向 ISP 请求许可, 或事先知会的情况下进行的, 因而如果被发现, 有时可能会成为 ISP 撤销您的账号的一个借口。 商业用户一般来说会购买昂贵得多的 Internet 线路, 通常会获得一组长期有效的静态 IP 地址块。 ISP 一般会希望并同意商业用户在他们的内网中使用 NAT。
在 IP 地址空间中, 有一些特殊的范围是保留供经过 NAT 的内网 LAN IP 地址使用的。 根据 RFC 1918, 您可以使用下面这些 IP 范围用于内网, 它们不会在 Internet 公网上路由:
NAT 规则是通过 ipnat 命令加载的。 默认情况下, NAT 规则会保存在 /etc/ipnat.rules 文件中。 请参见 ipnat(1) 了解更多的详情。
如果在 NAT 已经启动之后想要修改 NAT 规则, 可以修改保存 NAT 规则的那个文件, 然后在执行 ipnat
命令时加上 -CF
参数, 以删除在用的 NAT 内部规则表, 以及所有地址翻译表中已有的项。
要重新加载 NAT 规则, 可以使用类似下面的命令:
# ipnat -CF -f /etc/ipnat.rules
如果想要看看您系统上 NAT 的统计信息, 可以用下面的命令:
# ipnat -s
要列出当前的 NAT 表的映射关系, 使用下面的命令:
# ipnat -l
要显示详细的信息并显示与规则处理和当前的规则/表项:
# ipnat -v
NAT 规则非常的灵活, 能够适应商业用户和家庭用户的各种不同的需求。
这里所介绍的规则语法已经被简化, 以适应非商用环境中的一般情况。 完整的规则语法描述, 请参考 ipnat(5) 联机手册中的介绍。
NAT 规则的写法与下面的例子类似:
map IF LAN_IP_RANGE -> PUBLIC_ADDRESS
关键词 map 出现在规则的最前面。
将 IF 替换为对外的网络接口名。
LAN_IP_RANGE 是内网中的客户机使用的地址范围。 通常情况下, 这应该是类似 192.168.1.0/24 的地址。
PUBLIC_ADDRESS 既可以是外网的 IP 地址, 也可以是 0/32 这个特殊的关键字, 它表示分配到 IF 上的所有地址。
当包从 LAN 到达防火墙, 而目的地址是公网地址时, 它首先会通过 outbound 过滤规则。 接下来, NAT 会得到包, 并按自顶向下的顺序处理规则, 而第一个匹配的规则将生效。 NAT 接下来会根据包对应的接口名字和源 IP 地址检查所有的规则。 如果包和某个 NAT 规则匹配, 则会检查包的 [源 IP 地址, 例如, 内网的 IP 地址] 是否在 NAT 规则中箭头左侧指定的 IP 地址范围匹配。 如果匹配, 则包的原地址将被根据用 0/32 关键字指定的 IP 地址重写。 NAT 将向它的内部 NAT 表发送此地址, 这样, 当包从 Internet 公网中返回时, 就能够把地址映射回原先的内网 IP 地址, 并在随后使用过滤器规则来处理。
要启用 IPNAT, 只需在 /etc/rc.conf 中加入下面一些语句。
使机器能够在不同的网络接口之间进行包的转发, 需要:
gateway_enable="YES"
每次开机时自动启动 IPNAT:
ipnat_enable="YES"
指定 IPNAT 规则集文件:
ipnat_rules="/etc/ipnat.rules"
对于在一个 LAN 中有大量 PC, 以及包含多个 LAN 的情形, 把所有的内网 IP 地址都映射到同一个公网 IP 上会导致资源不够的问题, 因为同一个端口可能在许多做了 NAT 的 LAN PC 上被多次使用, 并导致碰撞。 有两种方法来缓解这个难题。
普通的 NAT 规则类似于:
map dc0 192.168.1.0/24 -> 0/32
上面的规则中, 包的源端口在包通过 IPNAT 时时不会发生变化的。 通过使用 portmap 关键字, 您可以要求 IPNAT 只使用一定范围内的端口地址。 比如说, 下面的规则将让 IPNAT 把源端口改为指定范围内的端口:
map dc0 192.168.1.0/24 -> 0/32 portmap tcp/udp 20000:60000
使用 auto 关键字可以让配置变得更简单一些, 它会要求 IPNAT 自动地检测可用的端口并使用:
map dc0 192.168.1.0/24 -> 0/32 portmap tcp/udp auto
对于很大的 LAN 来说, 总会有一个临界值, 此时 LAN 的地址会多到没办法使用一个公网地址表达的程度。 这时, 类似下面的规则需要进行修改:
map dc0 192.168.1.0/24 -> 204.134.75.1
目前的这个规则, 将所有的链接都通过 204.134.75.1 来映射。 可以把它改为一个范围:
map dc0 192.168.1.0/24 -> 204.134.75.1-10
或者使用 CIDR 记法指定的一组地址:
map dc0 192.168.1.0/24 -> 204.134.75.0/24
非常流行的一种做法是, 将 web 服务器、 邮件服务器、 数据库服务器以及 DNS 分别放到 LAN 上的不同的 PC 上。 这种情况下, 来自这些服务器的网络流量仍然应该被 NAT, 但必须有办法把进入的流量发到对应的局域网的 PC 上。 IPNAT 提供了 NAT 重定向机制来解决这个问题。 考虑下面的情况, 您的 web 服务器的 LAN 地址是 10.0.10.25, 而您的唯一的公网 IP 地址是 20.20.20.5, 则可以编写这样的规则:
rdr dc0 20.20.20.5/32 port 80 -> 10.0.10.25 port 80
或者:
rdr dc0 0/32 port 80 -> 10.0.10.25 port 80
另外, 也可以让 LAN 地址 10.0.10.33 上运行的 LAN DNS 服务器来处理公网上的 DNS 请求:
rdr dc0 20.20.20.5/32 port 53 -> 10.0.10.33 port 53 udp
FTP 是一个在 Internet 如今天这样为人所熟知之前就已经出现的恐龙, 那时, 研究机构和大学是通过租用的线路连到一起的, 而 FTP 则被用于在科研人员之间共享大文件。 那时, 数据的安全性并不是需要考虑的事情。 若干年之后, FTP 协议则被埋进了正在形成中的 Internet 骨干, 而它使用明文来交换用户名和口令的缺点, 并没有随着新出现的一些安全需求而得到改变。 FTP 提供了两种不同的风格, 即主动模式和被动模式。 两者的区别在于数据通道的建立方式。 被动模式相对而言要更加安全, 因为数据通道是由发起 ftp 会话的一方建立的。 关于 FTP 以及它所提供的不同模式, 在 http://www.slacksite.com/other/ftp.html 进行了很好的阐述。
IPNAT 提供了一个内建的 FTP 代理选项, 它可以在 NAT map 规则中指定。 它能够监视所有外发的 FTP 主动或被动模式的会话开始请求, 并动态地创建临时性的过滤器规则, 只打开用于数据通道的端口号。 这样, 就消除了 FTP 一般会给防火墙带来的, 需要大范围地打开高端口所可能带来的安全隐患。
下面的规则可以处理来自内网的 FTP 访问:
map dc0 10.0.10.0/29 -> 0/32 proxy port 21 ftp/tcp
这个规则能够处理来自网关的 FTP 访问:
map dc0 0.0.0.0/0 -> 0/32 proxy port 21 ftp/tcp
这个则处理所有来自内网的非 FTP 网络流量:
map dc0 10.0.10.0/29 -> 0/32
FTP map 规则应该在普通的 map 规则之前出现。 所有的包会从最上面的第一个规则开始进行检查。 匹配的顺序是网卡名称, 内网源 IP 地址, 以及它是否是 FTP 包。 如果所有这些规则都匹配成功, 则 FTP 代理将建立一个临时的过滤规则, 以便让 FTP 会话的数据包能够正常出入, 同时对这些包进行 NAT。 所有的 LAN 数据包, 如果没有匹配第一条规则, 则会继续尝试匹配下面的规则, 并最终被 NAT。
如果使用了 NAT FTP 代理, 则只需要为 FTP 创建一个规则。
如果没有使用 FTP 代理, 则需要下面三个规则:
# Allow out LAN PC client FTP to public Internet # Active and passive modes pass out quick on rl0 proto tcp from any to any port = 21 flags S keep state # Allow out passive mode data channel high order port numbers pass out quick on rl0 proto tcp from any to any port > 1024 flags S keep state # Active mode let data channel in from FTP server pass in quick on rl0 proto tcp from any to any port = 20 flags S keep state
在 IPFILTER 的 3.4.31 版中, 直到整个 FTP 会话直到将要结束之前, FTP 代理都会像文档中介绍的那样。 但一旦有一方要求关闭会话, 则来自远程 FTP 服务器 21 端口的一个包会被阻止, 并在日志中记下这一情况。 NAT FTP/代理显然过早地删除了临时规则, 而不是等待远程 FTP 服务器确认它。 有人就这个问题在 IPF 的邮件列表中发表了问题报告。
解决这个问题的方法是, 要么增加一条过滤规则来摆脱不希望的日志消息, 或者什么都不做, 并忽略日志中的 FTP 连入错误信息。 绝大多数人并不经常使用外部的 FTP 服务。
block in quick on rl0 proto tcp from any to any port = 21
本文档和其它文档可从这里下载:ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.
如果对于FreeBSD有问题,请先阅读文档,如不能解决再联系<[email protected]>.
关于本文档的问题请发信联系 <[email protected]>.