简介
实施防火墙是保护服务器的重要步骤。其中很大一部分是决定强制执行对网络流量的限制的个别规则和策略。像 iptables
这样的防火墙还允许您对应用规则的结构框架发表意见。
在本指南中,我们将构建一个可以作为更复杂规则集基础的防火墙。这个防火墙主要关注于提供合理的默认设置,并建立一个鼓励易于扩展性的框架。我们将在 Ubuntu 14.04 服务器上演示这一点。
先决条件
在开始之前,您应该对您希望实施的防火墙策略有一个基本的了解。您可以按照本指南来更好地了解一些您应该考虑的事项。
为了跟随本指南,您需要访问一个配置了 sudo
权限的非 root 用户的 Ubuntu 14.04 服务器。您可以在我们的 Ubuntu 14.04 初始服务器设置指南中了解如何配置这种类型的用户。
当您完成后,请继续以下步骤。
安装持久防火墙服务
要开始,如果您还没有安装 iptables-persistent
包,您需要安装它。这将允许我们保存我们的规则集,并在启动时自动应用它们:
sudo apt-get update sudo apt-get install iptables-persistent
在安装过程中,您将被问及是否要保存当前规则。在这里选择 “yes”。我们将稍后编辑生成的规则文件。
本指南中 IPv6 的说明
在开始之前,我们应该简要讨论一下 IPv4 与 IPv6。iptables
命令只处理 IPv4 流量。对于 IPv6 流量,使用一个名为 ip6tables
的单独的伴随工具。规则存储在单独的表和链中。对于 iptables-persistent
,IPv4 规则写入和读取自 /etc/iptables/rules.v4
,而 IPv6 规则保存在 /etc/iptables/rules.v6
。
本指南假设您的服务器不主动使用 IPv6。如果您的服务不使用 IPv6,最安全的做法是完全阻止访问,正如我们在本文中将要做的那样。
实施基本防火墙策略(快速方式)
为了尽快开始运行,我们将向您展示如何直接编辑规则文件以复制并粘贴完成的防火墙策略。之后,我们将解释一般策略,并向您展示如何使用 iptables
命令来实现这些规则,而不是修改文件。
为了实施我们的防火墙策略和框架,我们将编辑 /etc/iptables/rules.v4
和 /etc/iptables/rules.v6
文件。使用 sudo
权限在文本编辑器中打开 rules.v4
文件:
sudo nano /etc/iptables/rules.v4
在文件中,您将看到类似以下内容的文件:
# Generated by iptables-save v1.4.21 on Tue Jul 28 13:29:56 2015 *filter :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] COMMIT # Completed on Tue Jul 28 13:29:56 2015
将内容替换为:
*filter # 允许所有出站流量,但默认情况下拒绝入站和转发数据包 :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] # 自定义协议链 :UDP - [0:0] :TCP - [0:0] :ICMP - [0:0] # 可接受的 UDP 流量 # 可接受的 TCP 流量 -A TCP -p tcp --dport 22 -j ACCEPT # 可接受的 ICMP 流量 # 标准接受策略 -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT -A INPUT -i lo -j ACCEPT # 拒绝无效数据包 -A INPUT -m conntrack --ctstate INVALID -j DROP # 将流量传递到特定协议链 ## 仅允许新连接(已建立和相关的连接应已处理) ## 对于 TCP,此外,仅允许新的 SYN 数据包,因为这是建立新 TCP 连接的唯一有效方法 -A INPUT -p udp -m conntrack --ctstate NEW -j UDP -A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP -A INPUT -p icmp -m conntrack --ctstate NEW -j ICMP # 拒绝任何已经到达此点的流量 ## 尝试使用协议特定的拒绝消息 -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable -A INPUT -p tcp -j REJECT --reject-with tcp-reset -A INPUT -j REJECT --reject-with icmp-proto-unreachable # 提交更改 COMMIT *raw :PREROUTING ACCEPT [0:0] :OUTPUT ACCEPT [0:0] COMMIT *nat :PREROUTING ACCEPT [0:0] :INPUT ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] COMMIT *security :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] COMMIT *mangle :PREROUTING ACCEPT [0:0] :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] COMMIT
保存并关闭文件。
您可以通过输入以下命令来测试文件的语法错误。在继续之前修复任何此命令显示的语法错误:
sudo iptables-restore -t /etc/iptables/rules.v4
接下来,打开 /etc/iptables/rules.v6
文件以修改 IPv6 规则:
sudo nano /etc/iptables/rules.v6
我们可以通过替换文件的内容为以下配置来阻止所有 IPv6 流量:
*filter :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT DROP [0:0] COMMIT *raw :PREROUTING DROP [0:0] :OUTPUT DROP [0:0] COMMIT *nat :PREROUTING DROP [0:0] :INPUT DROP [0:0] :OUTPUT DROP [0:0] :POSTROUTING DROP [0:0] COMMIT *security :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT DROP [0:0] COMMIT *mangle :PREROUTING DROP [0:0] :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT DROP [0:0] :POSTROUTING DROP [0:0] COMMIT
保存并关闭文件。
要测试此文件的语法错误,我们可以使用 ip6tables-restore
命令和 -t
选项:
sudo ip6tables-restore -t /etc/iptables/rules.v6
当两个规则文件都报告没有语法错误时,您可以通过输入以下命令来应用其中的规则:
sudo service iptables-persistent reload
这将立即实施文件中概述的策略。您可以通过列出当前使用的 iptables
规则来验证这一点:
sudo iptables -S sudo ip6tables -S
这些防火墙规则将在每次启动时重新应用。测试以确保您仍然可以登录,并且所有其他访问都被阻止。
我们一般防火墙策略的解释
在上述规则中构建的基本防火墙中,我们创建了一个可轻松调整以添加或删除规则的可扩展框架。对于 IPv4 流量,我们主要关注 filter
表中的 INPUT
链。此链将处理所有发送到我们服务器的数据包。我们还允许所有出站流量,并拒绝所有数据包转发,这只有在此服务器作为其他主机的路由器时才合适。我们在所有其他表中接受数据包,因为我们只想在本指南中过滤数据包。
一般来说,我们的规则建立了一个默认拒绝传入流量的防火墙。然后,我们开始创建例外,以排除我们希望从此策略中排除的服务和流量类型。
在主 INPUT
链中,我们添加了一些通用规则,用于处理我们确信始终以相同方式处理的流量。例如,我们始终希望拒绝被视为“无效”的数据包,并且我们始终希望允许本地回环接口上的流量以及与已建立连接相关的数据。
之后,我们根据流量使用的协议匹配流量,并将其转移到协议特定的链。这些协议特定的链旨在保存匹配并允许特定服务的规则。在此示例中,我们在 TCP
链中仅允许 SSH 服务。如果我们提供另一个服务,比如 HTTP(S) 服务器,我们也可以在这里添加例外。这些链将是大部分自定义的重点。
任何不符合通用规则或协议特定服务规则的流量都由 INPUT
链中的最后几条规则处理。我们将默认策略设置为防火墙的 DROP
,这将拒绝通过我们的规则的数据包。但是,INPUT
链末尾的规则会拒绝数据包并向客户端发送一条消息,模拟服务器在该端口上没有运行服务时的响应。
对于 IPv6 流量,我们简单地拒绝所有流量。我们的服务器没有使用此协议,因此最安全的做法是根本不与流量交互。
(可选) 更新域名服务器
阻止所有 IPv6 流量可能会干扰服务器在互联网上解析事物的方式。例如,这可能会影响您使用 APT。
如果您在运行 apt-get update
时遇到以下错误:
Err http://security.ubuntu.com trusty-security InRelease Err http://security.ubuntu.com trusty-security Release.gpg Could not resolve 'security.ubuntu.com' . . .
您应该按照以下步骤使 APT 再次正常工作。
首先,将您的域名服务器设置为外部域名服务器。以下示例使用了 Google 的域名服务器。打开 /etc/network/interfaces
进行编辑:
sudo nano /etc/network/interfaces
按照以下所示更新 dns-nameservers
行:
. . . iface eth0 inet6 static address 2604:A880:0800:0010:0000:0000:00B2:0001 netmask 64 gateway 2604:A880:0800:0010:0000:0000:0000:0001 autoconf 0 dns-nameservers 8.8.8.8 8.8.4.4
刷新您的网络设置:
sudo ifdown eth0 && sudo ifup eth0
预期输出为:
RTNETLINK answers: No such process Waiting for DAD... Done
接下来,创建一个新的防火墙规则,以在 IPv4 可用时强制使用 IPv4。创建这个新文件:
sudo nano /etc/apt/apt.conf.d/99force-ipv4
在文件中添加以下一行:
Acquire::ForceIPv4 "true";
保存并关闭文件。现在您应该能够使用 APT 了。
使用 IPTables 命令实现我们的防火墙
现在您了解了我们构建的策略背后的一般思想,我们将介绍如何使用 iptables
命令创建这些规则。我们最终将得到与上面指定的相同规则,但我们将通过逐步添加规则来创建我们的策略。因为 iptables
立即应用每个规则,所以规则排序非常重要(我们将拒绝数据包的规则留到最后)。
重置您的防火墙
我们将首先重置我们的防火墙规则,以便了解如何从命令行构建策略。您可以通过输入以下命令来清除所有规则:
sudo service iptables-persistent flush
您可以通过输入以下命令来验证您的规则是否已重置:
sudo iptables -S
您应该看到 filter
表中的规则已经消失,并且所有链上的默认策略都设置为 ACCEPT
:
[secondary_label output] -P INPUT ACCEPT -P FORWARD ACCEPT -P OUTPUT ACCEPT
创建特定协议的链
我们将首先创建所有特定协议的链。这些链将用于保存我们希望公开的服务的拒绝策略的规则。我们将为 UDP
流量创建一个链,为 TCP
创建一个链,为 ICMP
创建一个链:
sudo iptables -N UDP sudo iptables -N TCP sudo iptables -N ICMP
我们可以立即添加 SSH 流量的例外。SSH 使用 TCP,因此我们将添加一个规则,以接受目标端口为 22 的 TCP 流量到 TCP 链:
sudo iptables -A TCP -p tcp --dport 22 -j ACCEPT
如果我们想要添加其他 TCP 服务,现在可以通过替换端口号重复该命令。
创建通用接受和拒绝规则
在 INPUT
链中,所有传入流量开始过滤,我们需要添加我们的通用规则。这些是一些常识规则,通过接受低风险流量(本地流量和与我们已经检查的连接相关的流量)和拒绝明显无用的流量(无效数据包),为我们的防火墙设置基线。
首先,我们将创建一个例外,以接受所有属于已建立连接或与已建立连接相关的流量:
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
此规则使用 conntrack
扩展,提供内部跟踪,以便 iptables
具有评估数据包的上下文,作为较大连接的一部分,而不是作为一系列离散、不相关的数据包流的上下文。TCP 是基于连接的协议,因此已建立的连接相当明确定义。对于 UDP 和其他无连接的协议,已建立的连接指的是已经看到响应的流量(原始数据包的源将是响应数据包的目的地,反之亦然)。相关连接指的是与现有连接相关联的已启动的新连接。这里的经典示例是 FTP 数据传输连接,它将与已建立的 FTP 控制连接相关联。
我们还希望允许所有源自本地回环接口的流量。这是由服务器生成并发送到服务器的流量。它被主机上的服务用于彼此通信:
sudo iptables -A INPUT -i lo -j ACCEPT
最后,我们希望拒绝所有无效数据包。数据包可能因多种原因无效。它们可能指的是不存在的连接,可能被发送到不存在的接口、地址或端口,或者可能只是格式错误。无论如何,我们将丢弃所有无效数据包,因为没有适当的处理方式,它们可能代表恶意活动:
sudo iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
创建跳转规则到特定协议链
到目前为止,我们已经在INPUT
链中创建了一些通用规则,并在特定协议链中创建了一些特定可接受服务的规则。然而,现在,流量进入INPUT
链后,无法到达我们的特定协议链。
我们需要将INPUT
链中的流量定向到适当的特定协议链。我们可以根据协议类型进行匹配,将其发送到正确的链中。我们还将确保数据包代表一个新连接(任何已建立或相关的连接应该已经在之前处理过)。对于TCP数据包,我们将添加额外的要求,即数据包是SYN数据包,这是开始TCP连接的唯一有效类型:
sudo iptables -A INPUT -p udp -m conntrack --ctstate NEW -j UDP sudo iptables -A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP sudo iptables -A INPUT -p icmp -m conntrack --ctstate NEW -j ICMP
拒绝所有剩余流量
如果传递到特定协议链的数据包没有匹配任何规则,控制将被传回到INPUT
链。任何到达这一点的东西都不应该被我们的防火墙允许。
我们将使用REJECT
目标拒绝流量,这会向客户端发送一个响应消息。这使我们能够指定出站消息,以便我们可以模仿客户端尝试发送数据包到常规关闭端口时会收到的响应。响应取决于客户端使用的协议。
尝试到达关闭的UDP端口将导致ICMP“端口不可达”消息。我们可以通过输入以下内容来模仿这一行为:
sudo iptables -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
尝试在关闭端口上建立TCP连接会导致TCP RST响应:
sudo iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset
对于所有其他数据包,我们可以发送ICMP“协议不可达”消息,以指示服务器不响应该类型的数据包:
sudo iptables -A INPUT -j REJECT --reject-with icmp-proto-unreachable
调整默认策略
我们添加的最后三条规则应该处理INPUT
链中的所有剩余流量。然而,作为预防措施,我们应该将默认策略设置为DROP
。如果此服务器未配置为路由到其他机器,则还应在FORWARD
链中设置此策略:
sudo iptables -P INPUT DROP sudo iptables -P FORWARD DROP
为了匹配我们的IPv6策略,将所有流量丢弃,我们可以使用以下ip6tables
命令:
sudo ip6tables -P INPUT DROP sudo ip6tables -P FORWARD DROP sudo ip6tables -P OUTPUT DROP
这应该相当接近复制我们设置的规则。
保存IPTables规则
此时,您应该测试您的防火墙规则,并确保它们覆盖了您想要阻止的流量,同时不会妨碍您的正常访问。一旦您确信您的规则行为正确,您可以保存它们,这样它们将在系统启动时自动应用。
通过输入以下内容保存您当前的规则(IPv4和IPv6):
sudo service iptables-persistent save
这将覆盖您在命令行上制定的/etc/iptables/rules.v4
和/etc/iptables/rules.v6
文件中的策略。
结论
通过遵循本指南,无论是直接将防火墙规则粘贴到配置文件中,还是通过命令行手动应用和保存它们,您都已经创建了一个良好的起始防火墙配置。您将需要添加单独的规则来允许访问您想要提供的服务。
本指南中建立的框架应该使您能够轻松进行调整,并有助于澄清您现有的策略。查看我们的其他一些指南,了解如何使用一些流行的服务构建防火墙策略:
- Iptables基础知识:常见防火墙规则和命令
- 如何设置Iptables防火墙以保护服务器之间的流量
- 如何通过Iptables在Linux网关上转发端口
- 如何使用Nmap和Tcpdump测试您的防火墙配置