如何在UbuntuVPS上仅使用iptables配置端口敲门

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

状态: 已弃用

本文介绍了不再受支持的 Ubuntu 版本。 如果您当前正在运行运行 Ubuntu 12.04 的服务器,我们强烈建议您升级或迁移到受支持的 Ubuntu 版本:

原因: Ubuntu 12.04 已于 2017 年 4 月 28 日终止生命周期 (EOL) and no longer receives security patches or updates. This guide is no longer maintained.

请参阅: 本指南可能仍可用作参考,但可能不适用于其他 Ubuntu 版本。 如果可用,我们强烈建议使用为您正在使用的 Ubuntu 版本编写的指南。 您可以使用页面顶部的搜索功能来查找更新的版本。


介绍


连接到 Internet 的服务器会受到恶意用户、脚本和自动机器人的各种攻击和探测。 有时,在不影响对服务和资源的合法访问的情况下,保护您的服务器免受攻击是一种平衡行为。

某些类型的服务旨在对公共互联网可见和可消费。 Web 服务器就是一个例子。 其他类型的服务通常仅由系统管理员或选定数量的个人使用,并不意味着是公共资源。

称为 端口敲击 的概念是一种适合后一种描述的屏蔽过程的方式。 端口敲门的工作原理是覆盖与防火墙后面的进程相关联的端口,直到发生特定的、预定的网络活动序列。 此时,端口敲门服务重新配置防火墙以允许访问受保护的应用程序。

在上一篇文章中,我们讨论了如何通过专门设计的端口敲门服务启用端口敲门。 在本文中,我们将讨论配置端口敲击的另一种方法。

此方法不依赖外部应用程序来更改防火墙规则。 相反,iptables 防火墙可以利用称为“最近”的状态跟踪模块在防火墙规则本身内完成所有这些工作。

我们将在 Ubuntu 12.04 droplet 上进行配置,但任何类型的 Linux 服务器都应该以类似的方式运行。

笔记: 本教程涵盖 IPv4 安全性。 在 Linux 中,IPv6 的安全性与 IPv4 分开维护。 例如,“iptables”仅维护 IPv4 地址的防火墙规则,但它有一个称为“ip6tables”的 IPv6 对应项,可用于维护 IPv6 网络地址的防火墙规则。

如果您的 VPS 配置为 IPv6,请记住使用适当的工具保护您的 IPv4 和 IPv6 网络接口。 有关 IPv6 工具的更多信息,请参阅本指南:如何配置工具以在 Linux VPS 上使用 IPv6

iptables 端口敲击概述


在我们进入实际配置之前,我们将描述最近的模块是如何工作的,以及它如何让我们创建一个端口敲门系统。

iptables 可以加载带有 -m 标志的模块。 最近的模块可以跟踪连接状态。 我们可以使用它来通过一系列链来汇集我们的端口连接尝试,具体取决于连接用户是否已经访问了每个先前需要的端口。

对于我们的示例,我们将阻止来自互联网的 SSH 守护程序,由我们的 eth0 接口表示。 一旦一系列端口被依次“敲开”,我们将动态重新配置我们的防火墙,以暂时允许来自我们计算机的 SSH 连接。

我们将用于本教程的顺序是:

  • 1111
  • 2222
  • 3333

这些值不应在实际配置中使用。

为了让用户正确验证并导致 iptables 将 SSH 守护进程暴露给他们的 IP 地址,他们必须按顺序访问这些端口中的每一个,而不会在其间发送非序列流量。 如果我们实现这一点,我们将成功创建一个端口敲击系统。

iptables 配置策略


为了实现上述设计,我们将使用几个不同的链。

首先,我们将接受我们不希望受到端口敲击的所有流量。 这包括任何公共资源,如 Web 服务器,以及已建立的连接和来自本地接口的连接。

紧接着,我们会将之前规则未处理的所有传入流量重定向到一个新链中,我们将在其中放置大部分规则。 在实施大型、独立的规则集时,这始终是一个好主意,因为它提供了一种非常简单的方法来启用或禁用该功能,只需将流量汇集到或绕过新链。

我们将此链称为 KNOCKING

我们还将使用其他一些链。 最近的模块允许我们对不同类型的流量进行分类,然后检查连接是否与之前设置的类别匹配。

我们将使用此策略来标记将数据包发送到第一个敲门目标的 IP 地址。 我们将有一个规则来检查第一个标志并检查第二个数据包是否正在发送到第二个敲击目标。 如果是,它会设置另一个标志,表明到目前为止它已经得到了两个答案。 如果第二个数据包与第二个目标不匹配,则将其丢弃并重置标志。

额外的链使用相同的策略来检查适当的标志,如果它继续在正确的路径上,则将其传递出去。 最后,一旦按顺序请求了最后一个数据包,SSH 守护进程就会短暂暴露,仅针对成功敲门的 IP 地址。 然后它会自动再次隐藏。

所以基本上,如果我们将我们的港口敲门规则视为进入我们服务的不同大门,我们将为我们的三个敲门提供三个大门。 这意味着请求 IP 地址可能位于 四个 不同的位置:

  • 初始状态:这是所有IP地址在成功向第一个敲击目标发送数据包之前所处的状态。 这不会由最近的模块设置,只是一种引用没有设置标志的客户端的方式。
  • auth1 state:成功敲入第一个敲门目标的地址被标记为“auth1”。 从这里,来自该主机的下一个数据包确定该地址是否将被放回初始状态或移动到“auth2”状态。
  • auth2 state:标记为该状态的地址已成功依次敲击第一个和第二个目标。 来自该主机的下一个数据包确定主机是否将设置回初始状态或设置为“auth3”状态。
  • auth3 状态:已标记为“auth3”的地址已在分配的时间内按顺序成功敲击所有三个端口。 如果标记为“auth3”的地址现在尝试访问服务(在提供的窗口中),则连接将被接受。 如果收到任何其他流量,该地址将被扔回初始状态。

除了这些将由最近模块设置的标志的状态之外,每个门或决策点都将被实现为一个链。 链可以概括如下:

  • GATE1:确定是否应将处于初始状态的地址标记为“auth1”。
  • GATE2:确定处于“auth1”状态的地址是否应该被处理为“auth2”或重置为“初始”状态。
  • GATE3:确定是否应将处于“auth2”状态的地址标记为“auth3”以允许 SSH 连接,或重置为“初始”状态。
  • PASSED:该链用于为成功敲门的客户端短暂打开 SSH 守护进程的端口。 来自此链中的客户端的任何不发往 SSH 守护程序的流量都会被丢弃,从而导致状态再次重置为“初始”。

如您所见,我们在这里有很多决策点。 KNOCKING[X41X] 链及其子链中的每种流量都应该被丢弃(除了来自成功敲击客户端的 SSH 守护进程的流量),无论它是否匹配正确的端口。 内部标记是唯一可以跟踪成功尝试的东西,并且此逻辑不会暴露给客户端。

这是端口敲门实现有效所必需的。 尝试联系的人不应该收到关于他们处于流程的哪个阶段的反馈,或者即使这样的机制已经到位。

配置常规防火墙框架


我们将首先为我们的连接建立一个基本框架。 我们上面讨论的策略将应用于处理所有传入连接的 INPUT 链。

我们将首先刷新现有的防火墙规则,以便我们可以从头开始。 在刷新规则之前,重新声明空表中的默认策略是“接受”始终是一个好主意,以维护您当前的连接:

sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -F

这将确保我们从一个完全开放的防火墙开始,我们可以开始限制它。

不过,在我们开始限制之前,我们要添加我们将使用的其他链:

sudo iptables -N KNOCKING
sudo iptables -N GATE1
sudo iptables -N GATE2
sudo iptables -N GATE3
sudo iptables -N PASSED

此时,我们应该有八个不同的链! 我们将使用所有这些,除了 OUTPUT 和 FORWARD 链,在这种情况下与我们无关。

首先,我们应该将不想通过端口敲门处理的流量添加到 INPUT 链中。 我们可以从接受所有当前连接开始,这将允许我们当前的 SSH 连接不受影响:

sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

我们还应该接受来自本地机器的所有连接,因为服务通常需要相互通信:

sudo iptables -A INPUT -i lo -j ACCEPT

如果您的服务应保持在外部且可公开访问,例如 Web 服务器,请添加规则以允许使用以下格式的此类连接:

sudo iptables -A INPUT -p协议--dport端口-j ACCEPT

在我们的示例中,我们假设我们有一个在默认端口 80 上运行的 Web 服务器,并且我们将允许此流量:

sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT

现在我们已经配置了基本的允许连接,我们可以将上述规则中未处理的所有流量转移到我们的 KNOCKING 链来执行实际的敲门逻辑:

sudo iptables -A INPUT -j KNOCKING

配置第一道门


当我们完成配置“门”时,我们会将它们全部连接到敲门链中,以引导流量通过我们的逻辑测试。 不过,在我们这样做之前,我们应该开发我们各自的逻辑单元。

我们将从定义我们的初始爆震测试开始。 当客户端连接时,我们只需要查看他们是否正在向我们的第一个目标发送数据包。 如果是,我们将客户端标记为通过第一个测试并断开连接。 如果不是,我们只需断开连接。

我们将首先在正确的端口尝试上设置标志:

sudo iptables -A GATE1 -p tcp --dport 1111 -m recent --name AUTH1 --set -j DROP

这条线做了很多事情。 首先,它将规则附加到 GATE1 链。 当使用的协议是“tcp”并且它尝试访问的端口是“1111”时,该规则将匹配,这是我们的第一个敲门目标。

如果这是真的,最近的模块(使用 -m recent 调用)将请求 IP 地址标记为名称 AUTH1(使用 --name AUTH1 --set 规则)。 这是我们将用来查看第二次敲击是否匹配的标志。 设置标志后数据包被丢弃,因此客户端不知道是否发生了任何事情。

接下来,我们将丢弃所有其他数据包,因为此时发送到此链的任何信息都是 only 寻找匹配的第一个数据包:

sudo iptables -A GATE1 -j DROP

我们现在已经覆盖了第一个端口。 如果请求了正确的端口,规则要么匹配并标记地址,要么不采取任何措施而简单地丢弃数据包。

配置第二道门


第二个门的配置方式与第一个大致相同。 不过,它 稍微复杂一些。

首先,将流量发送到该链的逻辑将存在于主 KNOCKING 链中。 该链不必检查第一个门设置的标志是否匹配,因为那已经发生了。

但是,它 确实 必须在开始处理之前删除任何标志。 如果它没有删除标记,那么一个地址可能会被标记为不同的名称,这可能会导致该地址只需扫描您的端口 3 次即可成功敲门。 这绝对不是我们想要的。

我们将在初步规则中再次使用最近的模块,这次只是为了清除名称:

sudo iptables -A GATE2 -m recent --name AUTH1 --remove

这是一个处理规则,所以我们不做任何决定或跳转。 我们只需剥离当前标志(允许流量进入此链的标志)并将其发送到下一条规则。

现在地址已经超过了这条链中的第一条规则,它处于没有标记的干净状态。 此时,我们检查此连接尝试是否与下一个端口目标正确匹配:

sudo iptables -A GATE2 -p tcp --dport 2222 -m recent --name AUTH2 --set -j DROP

这与第一个门的处理方式大致相同。 我们简单地设置 AUTH2 标志,表明请求地址通过了第二次测试,如果正确的端口被敲开。 该标志被设置,数据包再次被丢弃,客户端不知道他们的进度。

下一条规则起初可能看起来有点奇怪。

sudo iptables -A GATE2 -j GATE1

您可能会认为此时仅丢弃数据包是合乎逻辑的做法。 然而,这会在一个特定的情况下导致尴尬的局面。

如果我们在这里有一个“全部丢弃”规则,并且此时发送的数据包与 first 敲击目标匹配,则它不会被注册为敲击序列的开始。

例如,如果我们的客户端不小心点击了第一个端口两次,它不会注册为正确的,因为防火墙会看到这样的序列:

  • 第一个端口命中。 防火墙标志着第一次测试通过。 接下来将检查第二个端口。
  • 第一个端口命中。 不匹配第二个端口规则。 序列被重置。 接下来将检查第一个端口。
  • 第二个端口命中。 不匹配第一个端口规则。 序列被重置。 接下来将检查第一个端口。
  • 第三个端口被击中。 不匹配第一个端口规则。 序列被重置。 接下来将检查第一个端口。

如您所见,第一、第二、第三序列已经完成,但是防火墙对它应该检查的规则感到困惑。 在这种情况下,客户端必须在开始真正的序列之前发送一个虚拟请求,以便在出现错误时重置链。

为了避免这种情况,我们将 not 只是在此时丢弃数据包并完成它。 相反,我们将利用我们已经配置的 GATE1 链。 正如我们上面演示的,我们可以简单地将与第二个敲门目标不匹配的流量再次发送到第一个门:

sudo iptables -A GATE2 -j GATE1

这将导致客户在序列中的位置以两种方式之一重置。 如果请求是针对第一个端口的,则序列重新启动到作为成功的第一次敲门。 如果它不是第一个端口,则照常丢弃。 这样就避免了上述情况。

配置第三道门


我们可以使用我们从第二个门中学到的东西以相同的方式实现第三个门。

首先,我们要清除所有已分配给我们地址的标志,以便通过链的运行将设置正确的状态,而不会出现以前的陈旧标志:

sudo iptables -A GATE3 -m recent --name AUTH2 --remove

接下来,我们将测试连接尝试是否与第三个敲击目标匹配。 如果是,我们设置 AUTH3 标志,这表明客户端成功完成了所有需要的敲门。 像往常一样,我们之后丢弃数据包。

sudo iptables -A GATE3 -p tcp --dport 3333 -m recent --name AUTH3 --set -j DROP

从这里,我们再次将与第三次敲击目标不匹配的流量发送回第一个门,看看它是否应该算作一次成功的第一次敲击以重新启动序列:

sudo iptables -A GATE3 -j GATE1

此时,完成正确敲击序列的客户端应该被标记为 AUTH3,这将让我们在 PASSED 链中轻松为他们打开服务。

配置传递链


该链用于向成功敲击正确序列的客户端打开 SSH 守护进程 30 秒。

我们以与其他人相同的方式开始。 SSH 守护程序仅在客户端发送的下一个数据包请求它时才可用。 这迫使那些随机尝试通过敲门的人在每次尝试之间尝试 SSH 连接。

首先,我们进行通常的标志重置:

sudo iptables -A PASSED -m recent --name AUTH3 --remove

接下来,我们接受来自已进入此链的用户的 SSH 连接:

sudo iptables -A PASSED -p tcp --dport 22 -j ACCEPT

再一次,我们将所有不匹配的流量通过我们的第一个链发送回来,以查看它是否与第一个端口敲击目标匹配:

sudo iptables -A PASSED -j GATE1

现在,我们已经配置了所有子链,但我们的通用 KNOCKING 链将把流量传递到这些单独的链中。

配置敲链


现在我们已经配置了所有的子链,我们可以将它们连接到我们的通用 KNOCKING 链中,并创建如何传递流量的逻辑。

首先,我们会将成功完成所有敲门的客户端的流量直接传递到 PASSED 链中。

不过我们有一些选择。 我们可以实施一个时间限制,只给成功的客户端一个 30 秒的窗口来连接到守护进程。 之后,规则将不再匹配成功。

sudo iptables -A KNOCKING -m recent --rcheck --seconds 30 --name AUTH3 -j PASSED

接下来,我们将测试从最严格到最不严格的每个其他标志。 我们也可以在前一次敲门到期之前添加 10 秒的时间限制。 这将要求我们的客户端在 10 秒内完成下一阶段的敲门,然后在另外 30 秒内连接到 SSH 守护进程。

sudo iptables -A KNOCKING -m recent --rcheck --seconds 10 --name AUTH2 -j GATE3
sudo iptables -A KNOCKING -m recent --rcheck --seconds 10 --name AUTH1 -j GATE2

现在,我们想像往常一样将到目前为止尚未匹配的所有流量发送回 GATE1。 这将捕获任何第一次敲门的尝试:

sudo iptables -A KNOCKING -j GATE1

这基本上会通过将 GATE1 逻辑隐式添加到 KNOCKING 链的末尾来为我们的 KNOCKING 链设置默认丢弃策略。

在这一点上,我们所有的敲链都到位了。 我们添加了一些额外的结构来划分我们的逻辑。 整个 KNOCKING 链结构及其子链都连接到我们的常规输入链。

至此,我们的端口敲门机制就配置好了。 是时候测试一下了。

测试我们的端口敲击


有许多实用程序可用于生成我们的端口敲击配置所需的 TCP 数据包。 我们将使用 nmap 命令,因为它默认存在于大多数系统中。

Nmap 默认使用 TCP 数据包。 我们需要告诉它放弃其默认行为的主机发现部分,以便这些数据包不会干扰我们的敲门。 此外,我们希望我们的连接仅在一秒钟后超时,以便我们可以继续进行下一次敲门。

有了这些要求,单次敲击可能看起来像这样。 我们将以我们的第一个敲击目标为例:

nmap -Pn --host_timeout 201 --max-retries 0 -p 1111 your_server

因此,我们的整个敲击序列可以用这些命令来表示:

nmap -Pn --host_timeout 201 --max-retries 0 -p 1111 your_server nmap -Pn --host_timeout 201 --max-retries 0 -p 2222 your_server nmap -Pn --host_timeout 201 --max-retries 0 -p 3333你的服务器

然后我们将有 30 秒的时间来连接我们的 SSH 客户端。

我们可以利用一些非常基本的 bash 脚本来自动化这一点。 我们可以使用“for”循环遍历我们的端口序列,然后将其传递给 SSH 客户端:

 x 在 1111 2222 3333; 做 nmap -Pn --host_timeout 201 --max-retries 0 -p $x your_server && sleep 1; 完成 && ssh用户@your_server

这将敲第一个端口,等待一秒钟,敲下一个,依此类推,直到序列完成。 然后它将尝试连接服务器的 SSH 守护程序。

我们可以把它放在一个文件中来清理一下。 我们称之为 knock_client。 在文本编辑器中创建它:

纳米敲门客户
 #!/bin/bah
端口=“1111 2222 3333 ”主机=“你的服务器”
for x in $ports do nmap -Pn --host_timeout 201 --max-retries 0 -p $x $host sleep 1 done ssh user @${host}

保存并关闭文件。

使用以下命令使文件可执行:

chmod 755 knock_client

现在,我们可以通过键入以下内容连接到我们的服务器:

./knock_client

现在我们已经验证了我们的规则按预期工作,我们可以通过在我们的服务器上下载一个单独的包来使我们的防火墙规则持久化:

sudo apt-get install iptables-persistent

我们可以通过启用此服务在启动时应用我们的规则:

sudo service iptables-persistent start

结论


到目前为止,您应该拥有一个完全可操作的端口敲击系统,只使用 iptables 防火墙中包含的功能。 这有几个优点。 首先,iptables 是非常常用的,并且经常针对安全问题进行审计。 这意味着只要您的规则按照您 认为 的方式行事,它就应该是非常安全的。

与单独的敲门守护程序相比,此配置提供的另一个优点是敲门服务没有机会失败并使您被锁定在服务器之外。 如果 iptables 服务出现故障,您至少可以进入您的服务器进行修复。

贾斯汀·艾林伍德