当前位置:Linux教程 - Linux - Linux 2.4有状态防火墙设计(三)

Linux 2.4有状态防火墙设计(三)

第五章 有状态改进

明确关闭 ECN


我以前提到过应当关闭 ECN(明确拥塞通知),以便因特网通信可以正确工作。虽然您可能会按我的建议禁用了 ECN,但在将来您也许会忘了这样做。或者,您可能将防火墙脚本传送给某个人,而那个人启用了 ECN。由于这些原因,最好使用 /proc 接口来明确禁用 ECN,如下所示:


if [ -e /proc/sys/net/ipv4/tcp_ecn ]
then
echo 0 > /proc/sys/net/ipv4/tcp_ecn
fi


转发

如果使用 Linux 机器作为路由器,那么应该启用 IP 转发,它给予内核许可权,以允许包在 eth0 和 eth1 之间传递,反之亦然。在我们的配置示例中,eth0 连接到 LAN,eth1 连接到因特网,在允许 LAN 经由 Linux 机器连接因特网时,启用 IP 转发是必要步骤。要启用 IP 转发,请使用以下这行命令:


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


处理拒绝,第 1 部分


目前,我们已经删除了所有来自因特网的未经请求的通信流。虽然这是一种阻止讨厌的网络活动的有效方法,但是它有一些缺点。这种方法最大的问题是闯入者很容易就可以检测到我们正在使用防火墙,因为我们的机器没有应答标准 TCP 复位和 ICMP 端口不可到达响应 -- 一般机器发送会的响应,用于表示对不存在服务的连接失败。


处理拒绝,第 2 部分


与其让潜在的闯入者知道我们在运行防火墙(对于在提示他们,我们正在运行一些他们不能得到的有价值服务),还不如假装我们根本没有运行服务。通过将以下两个规则添加到 INPUT 链的末端,可以成功地完成此项任务:


iptables -A INPUT -p tcp -i eth1 -j REJECT --reject-with tcp-reset
iptables -A INPUT -p udp -i eth1 -j REJECT --reject-with icmp-port-unreachable

第一个规则负责正确传递 TCP 连接,而第二个规则处理 UDP。只要这两个规则就位,闯入者就很难检测到我们运行了防火墙;但愿,这会使闯入者离开我们的机器,转而搜索其它更潜在的目标以供他滥用。


处理拒绝,第 3 部分


除了使防火墙变得更“隐蔽”,这些规则还消除了由于连接到某些 ftp 和 irc 服务器带来的延迟。这个延迟是由于服务器对您的机器执行身份查找(连接到端口 113)而引起的,并最终(大约 15 秒之后)导致超时。现在,防火墙将返回 TCP 复位,身份查找将立即失败,而不是重试 15 秒(而您正在耐心地等待服务器的响应)。


防止欺骗


在许多发行版中,当建成网络接口时,还会将旧的 ipchains 规则添加到系统。这些特殊规则是由发行版的创建程序添加的,用于处理电子欺骗问题,即包的源地址已经过调整,这样它们就包含了无效值(某些脚本骗子做的事)。虽然我们可以创建类似的 iptables 规则来阻拦受到欺骗的包,但还有一种更简单的方法。目前,内核的内置功能可以删除受到欺骗的包;我们要做的只是通过简单的 /proc 接口来启用它。方法如下。

for x in lo eth0 eth1
do
echo 1 > /proc/sys/net/ipv4/conf/${x}/rp_filter
done

此 shell 脚本将告诉内核删除接口 lo、eth0 和 eth1 上所有受到欺骗的包。可以将这些行添加到防火墙脚本中,也可以将它们添加到创建 lo、eth0 和 eth1 接口的脚本中。


伪装


NAT(网络地址转换)和 IP 伪装虽然与防火墙没有直接关系,但通常与防火墙一起使用。我们将讨论您可能需要使用的两种常用 NAT/伪装配置。第一个规则负责处理那种用拨号链接到使用动态 IP 的因特网 (ppp0) 的情况:


iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE

如果您属于这种情况,那么还应该转换防火墙脚本,将对 ""eth1""(我们的示例 DSL 路由器)更改成 ""ppp0""。如果 ppp0 还不存在,最好添加引用 ""ppp0"" 的防火墙规则。只要创建了 ppp0,一切立即就会正常工作。请确保还启用了 IP 转发。


SNAT


如果使用 DSL 来连接因特网,那么您或许有两种可能配置中的一种。一种可能性是 DSL 路由器或调制解调器有其自己的 IP 号码,并为您执行网络地址转换。如果是这种情况,那么就不需要 Linux 来执行 NAT,因为 DSL 路由器已经这样处理了。

但是,如果想要更多地控制 NAT 功能,也许应该与 ISP 讨论关于 DSL 连接的配置,以便使您的 DSL 连接处于“桥接方式”。在桥接方式中,防火墙将成为 ISP 的网络中的正式部分,DSL 路由器将会在 ISP 和您的 Linux 机器之间透明的来回转发 IP 通信流,而不会让任何人知道它的存在。它不再拥有 IP 号码;事实上,eth1(在我们的示例中)隐藏了 IP。如果有人从因特网上 ping 您的 IP,他们将从您的 Linux 机器上得到应答,而不是路由器。


使用了这种设置,就应该使用 NAT(源 NAT),而不是伪装。以下就是您应该添加到防火墙的一行代码:


iptables -t nat -A POSTROUTING -o eth1 -j SNAT --to 1.2.3.4

在这个示例中,应该将 eth1 更改成直接连接到 DSL 路由器的以太网接口,1.2.3.4 应该更改成静态 IP(以太网接口的 IP)。再次声明,请记住要启用 IP 转发。


NAT 问题


幸好,NAT 和伪装与防火墙能够和睦相处。在编写防火墙过滤规则时,应忽略正在使用 NAT 的事实。您的规则应该根据包的“真正”源地址和目的地址接受、删除或拒绝它们。防火墙过滤代码能够看到包的原始源地址,以及最终目的地址。这对我们很有用处,因为它可以让防火墙继续正常工作,即使我们暂时禁用了 NAT 或伪装。


了解表


在以上的 NAT/伪装示例中,我们将规则附加到链,但还做了一些略有不同的事。请注意 ""-t"" 选项。""-t"" 选项可以让我们指定链所属的表。当省略这个选项时,缺省表将缺省为 ""filter""。因此,以前所有与非 NAT 相关的命令修改 ""filter"" 表中的 INPUT 链。""filter"" 表包含了所有与接收或拒绝包相关的规则,而 ""nat"" 表(如您假设的)包含了与网络地址转换相关的规则。还有其它内置 iptables 链,在 iptables 帮助页面以及 Rusty 的 HOWTO(请参阅本教程结尾处的“参考资料”部分,以获取链接)中详细描述了这些链。


增强的脚本


现在已经讨论过一些可能的增强,让我们看一下第二种更灵活的防火墙启动/停止脚本:


#!/bin/bash

# An enhanced stateful firewall for a workstation, laptop or router that isn
# running any network services like a web server, SMTP server, ftp server, etc.

#change this to the name of the interface that provides your ""uplink""
#(connection to the Internet)

UPLINK=""eth1""

#if you e a router (and thus should forward IP packets between interfaces),
#you want ROUTER=""yes""; otherwise, ROUTER=""no""

ROUTER=""yes""

#change this next line to the static IP of your uplink interface for static SNAT, or
#""dynamic"" if you have a dynamic IP. If you don need any NAT, set NAT to """" to
#disable it.

NAT=""1.2.3.4""

#change this next line so it lists all your network interfaces, including lo

INTERFACES=""lo eth0 eth1""

if [ ""$1"" = ""start"" ]
then
echo ""Starting firewall...""
iptables -P INPUT DROP
iptables -A INPUT -i ! ${UPLINK} -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp -i ${UPLINK} -j REJECT --reject-with tcp-reset
iptables -A INPUT -p udp -i ${UPLINK} -j REJECT --reject-with icmp-port-unreachable

#explicitly disable ECN
if [ -e /proc/sys/net/ipv4/tcp_ecn ]
then
echo 0 > /proc/sys/net/ipv4/tcp_ecn
fi

#disable spoofing on all interfaces
for x in ${INTERFACES}
do
echo 1 > /proc/sys/net/ipv4/conf/${x}/rp_filter
done

if [ ""$ROUTER"" = ""yes"" ]
then
#we e a router of some kind, enable IP forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward
if [ ""$NAT"" = ""dynamic"" ]
then
#dynamic IP address, use masquerading
echo ""Enabling masquerading (dynamic ip)...""
iptables -t nat -A POSTROUTING -o ${UPLINK} -j MASQUERADE
elif [ ""$NAT"" != """" ]
then
#static IP, use SNAT
echo ""Enabling SNAT (static ip)...""
iptables -t nat -A POSTROUTING -o ${UPLINK} -j SNAT --to ${UPIP}
fi
fi

elif [ ""$1"" = ""stop"" ]
then
echo ""Stopping firewall...""
iptables -F INPUT
iptables -P INPUT ACCEPT
#turn off NAT/masquerading, if any
iptables -t nat -F POSTROUTING
fi


第六章 有状态服务

查看规则


在开始定制防火墙以便可以在服务器上使用它之前,我需要演示如何列出当前活动的防火墙规则。要查看过滤器表的 INPUT 链中的规则,输入:


# iptables -v -L INPUT

-v 选项给出一个冗长的输出,这样我们可以查看每个规则传送的总包数和总的字节数。还可以使用以下命令查看 nat POSTROUTING 表:


# iptables -t nat -v -L POSTROUTING
Chain POSTROUTING (policy ACCEPT 399 packets, 48418 bytes)
pkts bytes target prot opt in out source destination
2728 170K SNAT all -- any eth1 anywhere anywhere to:215.218.215.2


准备提供服务


现在,防火墙不允许陌生人连接我们机器上的服务,因为它只接受进入 ESTABLISHED 或 RELATED 包。由于它删除了所有进入 NEW 包,因此所有连接尝试都将被无条件拒绝。但是,只要有选择地允许一些进入通信流通过防火墙,我们就可以让陌生人连接到我们指定的服务。

有状态 HTTP


虽然我们要接受一些进入连接,但我们可能并不想接受所有进入连接。最好从“缺省拒绝”策略开始(就象我们现在使用的策略),逐渐开放对那些希望人们可以连接的服务的访问。例如,如果正在运行 Web 服务器,我们允许 NEW 包进入我们的机器,只要它们去往端口 80 (HTTP)。那就是我们需要做的。一旦允许 NEW 包进入,那我们就允许建立连接。一旦建立了连接,就匹配了允许进入 ESTABLISHED 和 RELATED 包的现有规则,从而 HTTP 连接将变得畅通无阻。


有状态 HTTP 示例


让我们看一下防火墙的“核心”,以及允许进入 HTTP 连接的新规则:


iptables -P INPUT DROP
iptables -A INPUT -i ! ${UPLINK} -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
#our new rule follows
iptables -A INPUT -p tcp --dport http -m state --state NEW -j ACCEPT
iptables -A INPUT -p tcp -i ${UPLINK} -j REJECT --reject-with tcp-reset
iptables -A INPUT -p udp -i ${UPLINK} -j REJECT --reject-with icmp-port-unreachable

这个新规则允许去往我们机器的端口 80 (http) 的 NEW TCP 包进入。请注意这个规则的位置。它出现在 REJECT 规则有重要意义。由于 iptables 将应用第一个匹配的规则,因此将它放到 REJECT 行的后面会使这个规则无法生效。


最后的防火墙脚本


现在来看一下最后的防火墙脚本,它可以用于膝上型计算机、工作站、路由器或服务器(或者其中的某些组合!)。


#!/bin/bash

#Our complete stateful firewall script. This firewall can be customized for
#a laptop, workstation, router or even a server. :)

#change this to the name of the interface that provides your ""uplink""
#(connection to the Internet)

UPLINK=""eth1""

#if you e a router (and thus should forward IP packets between interfaces),
#you want ROUTER=""yes""; otherwise, ROUTER=""no""

ROUTER=""yes""

#change this next line to the static IP of your uplink interface for static SNAT, or
#""dynamic"" if you have a dynamic IP. If you don need any NAT, set NAT to """" to
#disable it.

NAT=""1.2.3.4""

#change this next line so it lists all your network interfaces, including lo

INTERFACES=""lo eth0 eth1""

#change this line so that it lists the assigned numbers or symbolic names (from
#/etc/services) of all the services that youd like to provide to the general
#public. If you don want any services enabled, set it to """"

SERVICES=""http ftp smtp ssh rsync""

if [ ""$1"" = ""start"" ]
then
echo ""Starting firewall...""
iptables -P INPUT DROP
iptables -A INPUT -i ! ${UPLINK} -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

#enable public access to certain services
for x in ${SERVICES}
do
iptables -A INPUT -p tcp --dport ${x} -m state --state NEW -j ACCEPT
done

iptables -A INPUT -p tcp -i ${UPLINK} -j REJECT --reject-with tcp-reset
iptables -A INPUT -p udp -i ${UPLINK} -j REJECT --reject-with icmp-port-unreachable

#explicitly disable ECN
if [ -e /proc/sys/net/ipv4/tcp_ecn ]
then
echo 0 > /proc/sys/net/ipv4/tcp_ecn
fi

#disable spoofing on all interfaces
for x in ${INTERFACES}
do
echo 1 > /proc/sys/net/ipv4/conf/${x}/rp_filter
done

if [ ""$ROUTER"" = ""yes"" ]
then
#we e a router of some kind, enable IP forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward
if [ ""$NAT"" = ""dynamic"" ]
then
#dynamic IP address, use masquerading
echo ""Enabling masquerading (dynamic ip)...""
iptables -t nat -A POSTROUTING -o ${UPLINK} -j MASQUERADE
elif [ ""$NAT"" != """" ]
then
#static IP, use SNAT
echo ""Enabling SNAT (static ip)...""
iptables -t nat -A POSTROUTING -o ${UPLINK} -j SNAT --to ${UPIP}
fi
fi

elif [ ""$1"" = ""stop"" ]
then
echo ""Stopping firewall...""
iptables -F INPUT
iptables -P INPUT ACCEPT
#turn off NAT/masquerading, if any
iptables -t nat -F POSTROUTING
fi