如何在Ubuntu14.04上使用Iptables实现基本防火墙模板

来自菜鸟教程
跳转至:导航、​搜索

介绍

实施防火墙是保护服务器的重要一步。 其中很大一部分是决定将对您的网络实施流量限制的个别规则和策略。 iptables 之类的防火墙还允许您对应用规则的结构框架有发言权。

在本指南中,我们将构建一个防火墙,它可以作为更复杂规则集的基础。 该防火墙将主要专注于提供合理的默认值并建立一个鼓励轻松扩展的框架。 我们将在 Ubuntu 14.04 服务器上进行演示。

先决条件

在开始之前,您应该对要实施的防火墙策略有一个基本的了解。 您可以按照 本指南 来更好地了解您应该考虑的一些事情。

为了继续进行,您需要能够访问 Ubuntu 14.04 服务器。 在本指南中,我们将使用配置了 sudo 权限的非 root 用户。 您可以在我们的 Ubuntu 14.04 初始服务器设置指南 中了解如何配置此类用户。

完成后,继续下面。

安装持久防火墙服务

要开始使用,您需要安装 iptables-persistent 软件包(如果您还没有安装的话)。 这将允许我们保存我们的规则集并在启动时自动应用它们:

sudo apt-get update
sudo apt-get install iptables-persistent

在安装过程中,系统会询问您是否要保存当前规则。 在这里说“是”。 我们将立即编辑生成的规则文件。

本指南中有关 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

在里面,你会看到一个看起来像这样的文件:

/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

将内容替换为:

/etc/iptables/rules.v4

*filter
# Allow all outgoing, but drop incoming and forwarding packets by default
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]

# Custom per-protocol chains
:UDP - [0:0]
:TCP - [0:0]
:ICMP - [0:0]

# Acceptable UDP traffic

# Acceptable TCP traffic
-A TCP -p tcp --dport 22 -j ACCEPT

# Acceptable ICMP traffic

# Boilerplate acceptance policy
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -i lo -j ACCEPT

# Drop invalid packets
-A INPUT -m conntrack --ctstate INVALID -j DROP

# Pass traffic to protocol-specific chains
## Only allow new connections (established and related should already be handled)
## For TCP, additionally only allow new SYN packets since that is the only valid
## method for establishing a new TCP connection
-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

# Reject anything that's fallen through to this point
## Try to be protocol-specific w/ rejection message
-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 the changes
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 流量:

/etc/iptables/rules.v6

*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

保存并关闭文件。

要测试此文件的语法错误,我们可以使用带有 -t 选项的 ip6tables-restore 命令:

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 流量可能会干扰您的服务器解决 Internet 问题的方式。 例如,这会影响您使用 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 行,如下所示:

/etc/网络/接口

. . .
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 可用时强制使用它。 创建这个新文件:

sudo nano /etc/apt/apt.conf.d/99force-ipv4

将这一行添加到文件中:

/etc/apt/apt.conf.d/99force-ipv4

Acquire::ForceIPv4 "true";

保存并关闭文件。 现在您应该可以使用 APT 了。

使用 IPTables 命令实现我们的防火墙

现在您已经了解了我们构建的策略背后的总体思路,我们将介绍如何使用 iptables 命令创建这些规则。 我们最终将得到与上面指定的相同规则,但我们将通过迭代添加规则来创建我们的策略。 因为 iptables 立即应用每个规则,所以规则顺序非常重要(我们将拒绝数据包的规则留到最后)。

重置您的防火墙

我们将首先重置我们的防火墙规则,以便我们可以看到如何从命令行构建策略。 您可以通过键入以下内容来刷新所有规则:

sudo service iptables-persistent flush

您可以通过键入以下内容来验证您的规则是否已重置:

sudo iptables -S

您应该看到 filter 表中的规则消失了,并且所有链上的默认策略都设置为 ACCEPT

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

警告

将您的策略设置为 DROP,如果您使用 sudo iptables -F 清除 iptables,您当前的 SSH 连接将被断开! 使用 sudo iptables-persistent flush 刷新是清除规则的更好方法,因为它也会重置默认策略。


为了匹配我们丢弃所有流量的 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 文件。

结论

按照本指南,通过将防火墙规则直接粘贴到配置文件中或通过在命令行中手动应用和保存它们,您已经创建了一个良好的启动防火墙配置。 您必须添加单独的规则以允许访问您想要提供的服务。

本指南中建立的框架应允许您轻松进行调整,并有助于澄清您现有的政策。 查看我们的其他一些指南,了解如何使用一些流行的服务构建您的防火墙策略: