防火墙实验:使用 iptables
Section outline
-
-
把 zip 文件下载到 VM 里, 然后用 unzip 命令解压缩。
-
-
在本任务中,我们将设置规则来防止外部机器访问路由器,ping 除外。请在路由器容器上执行以下 iptables 命令,然后尝试从 10.9.0.5 访问路由器。(1) 你能 ping 通路由器吗? (2) 你能 telnet 进入路由器吗(所有容器上都运行了 telnet 服务器,并且创建了一个名为 seed 的账户,密码为 dees)。 请报告你的观察结果,并解释每条规则的目的。
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT iptables -A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT iptables -P OUTPUT DROP → 设置 OUTPUT 的默认规则 iptables -P INPUT DROP → 设置 INPUT 的默认规则
清理。 在继续下一个任务前,请通过运行以下命令将 filter 表恢复到初始状态:
iptables -F iptables -P OUTPUT ACCEPT iptables -P INPUT ACCEPT
另一种恢复所有表状态的方法是重启容器。你可以使用以下命令(首先需要找到容器的 ID):
$ docker restart <Container ID>
-
在本任务中,我们将在路由器上设置防火墙规则,以保护内部网络 192.168.60.0/24 。我们需要使用 FORWARD 链来完成这一任务。INPUT 和 OUTPUT 链中的数据包方向是明确的:数据包要么进入(INPUT),要么出去(OUTPUT)。 但是,FORWARD 链不同,它是双向的:进入内部网络或从外部网络出去的所有数据包都会经过此链。为了指定方向,我们可以使用 "-i xyz" 选项(来自 xyz 接口)或 "-o xyz" 选项(从 xyz 接口出去)。你可以通过运行 "ip addr" 命令来得到接口名称。在本任务中,我们希望实现一个防火墙,来保护内部网络。具体来说,我们需要对 ICMP 流量实施以下限制:
- 外部主机不能 ping 内部主机
- 外部主机可以 ping 路由器
- 内部主机可以 ping 外部主机
- 内外部网络之间的其他数据包应当被阻止
你需要使用 "-p icmp"} 选项来设置与 ICMP 协议相关的规则。你可以运行 "iptables -p icmp -h" 来查看 ICMP 的规则说明。下面的例子会阻止 ICMP 回显请求。
iptables -A FORWARD -p icmp --icmp-type echo-request -j DROP
在你的实验报告中,请包含你的规则和截图,以证明防火墙的配置按预期工作。完成此任务后,请记得清理规则表,或在继续下一个任务之前重启容器。 -
在本任务中,我们将保护内部网络(192.168.60.0/24)中的 TCP 服务器。具体来说,我们希望实现以下目标:
- 所有内部主机都运行了一个 telnet 服务器(监听端口 23)。外部主机只能访问 192.168.60.5 上的 telnet 服务器,不能访问其他内部主机;
- 外部主机不能访问其他内部服务器;
- 内部主机可以访问所有内部服务器;
- 内部主机不能访问外部服务器;
- 在本任务中,不允许使用连接追踪机制,它将在后续任务中使用。
你可以使用 "-p tcp" 选项来设置与 TCP 协议相关的规则。你可以运行 "iptables -p tcp -h" 来查看 TCP 的规则说明。下面的例子允许来自接口 eth0 的源端口为 5000 的 TCP 数据包通过。iptables -A FORWARD -i eth0 -p tcp --sport 5000 -j ACCEPT
完成此任务后,请记得清理规则表,或在继续下一个任务之前重启容器。 -
在上几个任务中,我们只设置了无状态防火墙,逐个检查每个数据包。然而,数据包通常不是独立的;它们可能是某个 TCP 连接的一部分, 或者它们可能是由其他数据包触发的 ICMP 数据包。将数据包独立处理并不会考虑其上下文,因此可能导致不准确、不安全或复杂的防火墙规则。例如,如果我们希望仅允许在建立连接之后才能进入网络的 TCP 数据包,使用无状态数据包过滤器是无法轻松实现的,因为当防火墙检查每个单独的 TCP 数据包时, 它无法知道该数据包是否属于一个已建立的连接,除非防火墙为每个连接维护一些状态信息。如果防火墙这样做,它就变成了有状态防火墙。为了支持有状态防火墙,我们需要能够追踪连接。这是通过内核中的 conntrack 模块来实现的。在本任务中,我们将进行一些与该模块相关的实验,并熟悉连接追踪机制。在我们的实验中,我们将检查路由器容器上的连接追踪信息。可以使用以下命令进行操作:
# conntrack -L
本任务的目标是通过一系列实验帮助学生理解此追踪机制中的连接概念,特别是对于 ICMP 和 UDP 协议,因为与 TCP 协议不同,它们没有连接。请进行以下实验。每个实验后,请描述你的观察结果并解释。
ICMP 实验:运行以下命令并检查路由器上的连接追踪信息。描述你的观察结果。ICMP 连接状态保持多久?
// 在 10.9.0.5 上,发送 ICMP 数据包 # ping 192.168.60.5
UDP 实验:运行以下命令并检查路由器上的连接追踪信息。描述你的观察结果。UDP 连接状态保持多久?
// 在 192.168.60.5 上,启动一个 netcat UDP 服务器 # nc -lu 9090 // 在 10.9.0.5 上,发送 UDP 数据包 # nc -u 192.168.60.5 9090 <输入一些内容,然后回车>
TCP 实验:运行以下命令并检查路由器上的连接追踪信息。描述你的观察结果。TCP 连接状态保持多久?
// 在 192.168.60.5 上,启动一个 netcat TCP 服务器 # nc -l 9090 // 在 10.9.0.5 上,发送 TCP 数据包 # nc 192.168.60.5 9090 <输入一些内容,然后回车>
-
现在,我们准备根据连接设置防火墙规则。在以下示例中,"-m conntrack" 选项表示我们正在使用 conntrack 模块。这是 iptables 中非常重要的模块,它用于追踪连接,iptables 依赖于追踪信息来构建有状态防火墙。 “--ctstate ESTABLISHED,RELATED” 表示数据包是否属于 ESTABLISHED 或 RELATED 连接。 此规则允许属于现有连接的 TCP 数据包通过。
iptables -A FORWARD -p tcp -m conntrack \ --ctstate ESTABLISHED,RELATED -j ACCEPT
上面的规则不包括 SYN 数据包,因为这些数据包不属于任何已建立的连接。我们需要让创建连接的包通过。因此,我们需要添加一条规则来接受传入的 SYN 数据包:iptables -A FORWARD -p tcp -i eth0 --dport 8080 --syn \ -m conntrack --ctstate NEW -j ACCEPT
最后,我们将 FORWARD 链的默认策略设置为丢弃所有数据包。这样,如果数据包未被上述两条规则接受,它们将被丢弃。iptables -P FORWARD DROP
请重写任务 3 中的防火墙规则,但这次我们将添加一条允许内部主机访问任何外部服务器的规则(这是任务 3 中未允许的)。 在你使用连接追踪机制写出规则后,请思考如果不使用连接追踪机制该如何做(你不需要实际实现这些规则)。基于这两套规则,请比较这两种不同的方式,并解释每种方法的优缺点。完成此任务后,请记得清除所有规则。 -
除了阻止数据包之外,我们还可以限制通过防火墙的数据包数量。这可以使用 iptables 的 limit 模块来实现。在本任务中,我们将使用该模块来限制来自 10.9.0.5 的数据包进入内部网络的数量。你可以使用 "iptables -m limit -h" 来查看手册。
$ iptables -m limit -h limit match options: --limit avg max average match rate: default 3/hour [Packets per second unless followed by /sec /minute /hour /day postfixes] --limit-burst number number to match in a burst, default 5
请在路由器上运行以下命令,然后从 10.9.0.5 上 ping 192.168.60.5。描述你的观察结果。请分别在有和没有第二条规则的情况下进行实验,并解释是否需要第二条规则,及其原因。iptables -A FORWARD -s 10.9.0.5 -m limit \ --limit 10/minute --limit-burst 5 -j ACCEPT iptables -A FORWARD -s 10.9.0.5 -j DROP
-
iptables 功能非常强大。除了作为防火墙外,它还有许多其他应用。我们无法在这个实验中覆盖所有应用,但我们将实验其中之一--负载均衡。在本任务中,我们将使用它来对运行在内部网络中的三个 UDP 服务器进行负载均衡。首先,在每个主机上启动服务器:192.168.60.5、192.168.60.6 和 192.168.60.7(-k 选项表示该服务器可以接收来自多个主机的 UDP 数据报):
nc -luk 8080
我们可以使用 statistic 模块来实现负载均衡。你可以输入以下命令查看其手册。可以看到有两种模式:random 和 nth。我们将分别使用这两种模式进行实验。$ iptables -m statistic -h statistic match options: --mode mode Match mode (random, nth) random mode: [!] --probability p Probability nth mode: [!] --every n Match every nth packet --packet p Initial counter value (0 <= p <= n-1, default 0)
使用 nth 模式(轮询)。在路由器容器上,我们设置以下规则,适用于所有进入端口 8080 的 UDP 数据包。statistic 模块的 nth 模式被使用,它实现了轮询负载均衡策略:每三个数据包中,选择第一个数据包(即第 0 个),将其目标 IP 地址和端口号分别更改为 192.168.60.5 和 8080。修改后的数据包将继续其传输。
iptables -t nat -A PREROUTING -p udp --dport 8080 \ -m statistic --mode nth --every 3 --packet 0 \ -j DNAT --to-destination 192.168.60.5:8080
需要注意的是,未匹配此规则的数据包也将继续传输,它们不会被修改或阻止。设置此规则后,如果你发送一个 UDP 数据包到路由器的 8080 端口,你会看到每三分之一的数据包到达 192.168.60.5。
// 在 10.9.0.5 上 echo hello | nc -u 10.9.0.11 8080 <hit Ctrl-C>
请在路由器容器中添加更多规则,使所有三个内部主机接收到相同数量的数据包。请为这些规则提供一些解释。
使用 random 模式。让我们使用不同的模式来实现负载均衡。以下规则将按概率 P 选择匹配的数据包。你需要将 P 替换为一个概率值。
iptables -t nat -A PREROUTING -p udp --dport 8080 \ -m statistic --mode random --probability P \ -j DNAT --to-destination 192.168.60.5:8080
请使用此模式来实现你的负载均衡规则,使每个内部服务器都能获得大致相同的流量(虽然不一定完全相同,但在总包数足够大时应非常接近)。请提供一些关于这些规则的解释。