如何在 Ubuntu 14.04 上使用 Iptables 实现基本的防火墙模板
介绍
实施防火墙是保护服务器安全的重要一步。其中很大一部分是决定对您的网络实施流量限制的个人规则和政策。像 iptables
这样的防火墙还允许您对应用规则的结构框架有发言权。
在本指南中,我们将构建一个防火墙,它可以作为更复杂规则集的基础。该防火墙将主要侧重于提供合理的默认值并建立一个鼓励轻松扩展的框架。我们将在 Ubuntu 14.04 服务器上进行演示。
先决条件
在开始之前,您应该对要实施的防火墙策略有一个基本的了解。您可以按照本指南更好地了解您应该考虑的一些事情。
为了继续学习,您需要能够访问 Ubuntu 14.04 服务器。在本指南中,我们将使用配置有 sudo
权限的非根用户。您可以在我们的 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
在里面,你会看到一个看起来像这样的文件:
# 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
# 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 流量:
*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
行,如下所示:
. . .
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
将这一行添加到文件中:
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 \port unreachable 消息。我们可以通过键入以下内容来模仿:
- 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 \protocol unreachable 消息来指示服务器不响应该类型的数据包:
- 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
文件。
结论
按照本指南,将您的防火墙规则直接粘贴到配置文件中,或者通过在命令行上手动应用和保存它们,您已经创建了一个良好的起始防火墙配置。您将必须添加单独的规则以允许访问您想要提供的服务。
本指南中建立的框架应该允许您轻松进行调整,并有助于阐明您现有的政策。查看我们的一些其他指南,了解如何使用一些流行的服务构建您的防火墙策略:
- Iptables Essentials:常用防火墙规则和命令
- 如何设置 Iptables 防火墙以保护服务器之间的流量
- 如何使用 Iptables 通过 Linux 网关转发端口
- 如何使用 Nmap 和 Tcpdump 测试您的防火墙配置