介绍
实施防火墙是保护服务器的重要一步。其中很大一部分是在于对您的网络实施流量限制有决定性作用的个别规则和政策。防火墙iptables
也允许您对应用规则的结构框架有发言权。
在本指南中,我们将构建一个防火墙,它可以作为更复杂的规则集的基础。该防火墙将主要侧重于提供合理的默认设置并建立一个鼓励轻松扩展的框架。我们将在Ubuntu 14.04服务器上进行演示。
课程准备
在开始之前,您应该对要实施的防火墙策略有一个基本的了解。如果您使用的是腾讯云的CVM服务器,您可以直接在腾讯云控制台中的安全组进行设置。
为了跟进,您需要访问Ubuntu 14.04服务器。我们将使用sudo
在本指南中配置了权限的非root用户。没有服务器的同学可以在这里购买,不过我个人更推荐您使用免费的腾讯云开发者实验室进行试验,学会安装后再购买服务器。
完成后,继续下面。
安装持久防火墙服务
要开始使用,如果尚未安装包,则需要安装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
中被写入和读取,并且在/etc/iptables/rules.v6
中保留了IPv6规则。
本指南假定您未在服务器上主动使用IPv6。如果您的服务不利用IPv6,那么完全阻止访问会更安全,正如我们将在本文中所做的那样。
实施基本防火墙策略(快速方式)
为了尽快启动和运行,我们将向您展示如何直接编辑规则文件以复制和粘贴完成的防火墙策略。然后,我们将解释一般策略,并向您展示如何使用iptables
命令而不是修改文件来实现这些规则。
为了实现我们的防火墙策略和框架,我们将编辑/etc/iptables/rules.v4
和/etc/iptables/rules.v6
文件。使用sudo
权限在文本编辑器中打开rules.v4
文件:
sudo nano /etc/iptables/rules.v4
在里面,你会看到一个看起来像这样的文件:
代码语言:javascript复制# 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
将内容替换为:
代码语言:javascript复制*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
保存并关闭文件。
您可以通过键入此命令来测试文件的语法错误。修复在继续之前显示的任何语法错误:
代码语言:javascript复制sudo iptables-restore -t /etc/iptables/rules.v4
接下来,打开/etc/iptables/rules.v6
文件以修改IPv6规则:
sudo nano /etc/iptables/rules.v6
我们可以通过使用以下配置替换文件内容来阻止所有IPv6流量:
代码语言:javascript复制*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
当两个规则文件都没有报告语法错误时,您可以通过键入以下内容来应用规则:
代码语言:javascript复制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
刷新您的网络设置:
代码语言:javascript复制sudo ifdown eth0 && sudo ifup eth0
预期的输出是:
代码语言:javascript复制RTNETLINK answers: No such process
Waiting for DAD... Done
接下来,创建一个新的防火墙规则,以便在IPv4可用时强制使用IPv4。创建这个新文件:
代码语言:javascript复制sudo nano /etc/apt/apt.conf.d/99force-ipv4
将此单行添加到文件中:
代码语言:javascript复制Acquire::ForceIPv4 "true";
保存并关闭文件。现在你应该可以使用APT了。
使用IPTables命令实现我们的防火墙
现在您了解了我们构建的策略背后的一般概念,我们将介绍如何使用iptables
命令创建这些规则。我们将最终得到上面指定的相同规则,但我们将通过迭代添加规则来创建我们的策略。因为iptables
会立即应用每个规则,规则排序非常重要(我们保留拒绝数据包直到结束的规则)。
重置防火墙
我们将从重置防火墙规则开始,以便我们可以看到如何从命令行构建策略。您可以输入以下内容来清除所有规则:
代码语言:javascript复制sudo service iptables-persistent flush
您可以通过键入以下内容来验证是否重置了规则:
代码语言:javascript复制sudo iptables -S
您应该看到filter
表中的规则已经消失,并且在所有链上将默认策略设置为ACCEPT
:
-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链:
代码语言:javascript复制sudo iptables -A TCP -p tcp --dport 22 -j ACCEPT
如果我们想要添加额外的TCP服务,我们现在可以通过重复替换端口号的命令来实现。
创建通用接受和拒绝规则
在INPUT
链中,当所有传入流量都开始过滤,我们需要添加我们的通用规则。这些是一些常识规则,通过接受低风险的流量(本地流量和与我们已经检查过的连接相关的流量)和丢弃明显无效的流量(无效数据包)来设置防火墙的基线。
首先,我们将创建一个例外来接受作为已建立连接的一部分或与已建立连接相关的所有流量:
代码语言:javascript复制sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
此规则使用conntrack
扩展,该扩展提供内部跟踪,以便iptables
具有将数据包评估为较大连接的一部分而不是作为离散的无关数据包流所需的上下文。TCP是基于连接的协议,因此建立的连接定义相当明确。对于UDP和其他无连接协议,已建立的连接是指已看到响应的流量(原始数据包的源将是响应数据包的目标,反之亦然)。相关连接是指与现有连接关联启动的新连接。这里的典型示例是FTP数据传输连接,它与已经建立的FTP控制连接有关。
我们还希望允许所有流量源自本地环回接口。这是服务器生成并发往服务器的流量,主机上的服务将它用于彼此通信:
代码语言:javascript复制sudo iptables -A INPUT -i lo -j ACCEPT
最后,我们想要拒绝所有无效的数据包。由于多种原因,数据包可能无效。它们可能指的是不存在的连接,它们可能指向不存在的接口,地址或端口,或者它们可能只是格式不正确。在任何情况下,我们都会丢弃所有无效的数据包,因为没有正确的方法来处理它们,因为它们可能代表恶意活动:
代码语言:javascript复制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“端口无法访问”消息。我们可以通过输入以下内容来模仿:
代码语言:javascript复制sudo iptables -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
尝试在已关闭的端口上建立TCP连接会导致TCP RST响应:
代码语言:javascript复制sudo iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset
对于所有其他数据包,我们可以发送ICMP“协议不可达”消息,以指示服务器不响应该类型的数据包:
代码语言:javascript复制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):
代码语言:javascript复制sudo service iptables-persistent save
您在命令行上精雕细琢的政策将覆盖您的/etc/iptables/rules.v4
和/etc/iptables/rules.v6
文件。
结论
通过遵循本指南,通过将防火墙规则直接粘贴到配置文件中或通过在命令行上手动应用和保存它们,您就已创建了良好的启动防火墙配置。您必须添加单个规则才能访问要提供的服务。
本指南中建立的框架应该可以让您轻松进行调整,并有助于阐明您现有的政策。
更多Ubuntu教程请前往腾讯云 社区学习更多知识。
参考文献:《How To Implement a Basic Firewall Template with Iptables on Ubuntu 14.04》