为 Web 应用设置 iptables

`iptables` 软件包让我们能够对防火墙进行高级、精细的控制,并具有过滤和限制等重要的内置功能。本指南将介绍一种更高级的方法,利用上述功能、包含的 `conntrack` 模块和限制规则,同时探索潜在的安全风险。

本指南中使用的每个命令(“man”除外)都需要提升的权限,因此我在所有命令前面都添加了“sudo”,以便更容易地复制所有内容(当然,经过仔细检查之后)。

我们将实施遵循状态防火墙概念所规定的原则的防火墙。

  • 维基百科——状态防火墙
  • 本指南中介绍的配置已在本地 Ubuntu 24.04 虚拟机和云 Ubuntu 22.04 虚拟机上进行了测试。两者都安装了 Apache2 并启用了 HTTP/HTTPS 规则测试。测试包括发出和接收 HTTP/HTTPS 请求、检查“apt”连接、使用“dig”命令以及从客户端到虚拟机使用“ping”。

    此配置与 LAMP、LEMP 和 MERN 堆栈兼容,并且也应与其他类似的堆栈一起使用。

    最初,这是我的“设置 LAMP(堆栈)”指南的一部分,但我决定将其拆分为单独的出版物,以避免让新手感到不知所措。

    本指南也可在 Medium 上获取。

    手册和文档

    Terminal screenshot — iptables manual search preview

    我们鼓励您查看本指南中我们将使用的命令的文档。可以使用“man iptables”访问 Linux 命令文档和手册。

  • 使用向上和向下箭头键或 Page Up/Page Down 浏览手册。
  • 在手册打开时按 / 并输入搜索词以突出显示该术语或短语的实例。这对于新手来说尤其重要,因为长篇文档很快就会让人不知所措。
  • 要退出手册,请按 q。
  • 一些手册还提供了有关如何执行某些操作的示例。

    或者,您可以使用“iptables -h”获取摘要详细信息和选项列表。

    防火墙如何工作?

    所有防火墙环境都使用相同或相似的术语。概念保持不变。当我们设置防火墙规则时,我们会将这些规则添加到特定的 上。此时无需详细说明表格,因为通常情况下,在这种情况下,我们将仅处理默认的“过滤器”表。

    常见的连锁店有:

  • 输入 — 进入我们机器的数据包,
  • FORWARD — 数据包通过(主要用于路由器),
  • 输出 — 我们的机器发出的数据包。
  • 由于我们正在为 Web 服务器计算机设置防火墙,因此我假设您想要托管网站或其他 Web 应用程序。这意味着我们需要打开端口“80”(HTTP)和“443”(HTTPS)。最重要的是,我们需要打开 SSH/SFTP 端口“22”(SFTP 本质上是通过 SSH 的 FTP,这意味着它也使用端口“22”),以保持与我们的计算机的远程通信畅通。这些端口仅使用 TCP 协议。我们还将允许有限的 ICMP(ping)流量和 DNS 查找(通过 UDP 的端口“53”)。

    请记住,如果您更改了 SSH 端口,则必须使用自定义端口而不是默认的“22”!

    在标准防火墙解决方案中,规则的处理顺序与添加的顺序相同。在命令行界面 (CLI) 中,这一点尤其重要。在图形用户界面 (GUI) 中,通常允许您移动内容,但在这两种情况下,顺序错误都可能导致严重问题,包括完全锁定。

    如果您确实无法远程访问,大多数提供虚拟机的供应商也会提供 Web 终端或通过现场支持直接访问。如果不是这种情况,请不要羞于放弃安装并从头开始重试。这都是学习过程的一部分。

    使用 iptables

    正如您可能在手册中看到的,有很多命令、标志和参数。让我们来看看一些有用的基本命令。我们将跳过规则附加部分,因为我们稍后会详细介绍它们。

    要获取所有链的当前规则及其相应位置,请使用:

    sudo iptables -L -vn --line-numbers
  • -L 参数表示“列表”,列出所选链中的所有规则。如果没有指定链,则显示所有链。
  • -v 参数表示“详细”,它提供有关每个规则的更多详细信息。
  • -n 参数表示“数字”,这将阻止 iptables 尝试解析主机名并以数字形式显示 IP 地址和端口。
  • 最后,--line-numbers 选项会为输出中的每个规则添加行号。当您需要删除或修改特定规则时,这尤其有用。
  • 要从特定链中删除特定规则,您应该通过上一个命令查找它,然后使用:

    sudo iptables -D {chain} {rule-id}

    规则持久性

    直接通过 `iptables` 定义的规则是短暂的,这意味着它们是临时的,并且仅在会话期间存在。如果我们重新启动服务器而不保存它们,它们就会消失。

    因此,我们还需要“iptables”之上的“iptables-persistent”包。它基本上是一个“systemd”服务,每次机器启动时都会加载你的规则。

    要保存配置,我们可以使用以下命令:

    sudo netfilter-persistent save

    **危险:**规则添加后立即生效!此命令仅保存它们,以便在重启后重新加载它们!

    设置

    通常,您需要为特定端口设置 INPUT 和 OUTPUT 规则,以便您的服务器可以接收和响应来自互联网的请求。我们将更进一步。

    当有人访问我们的服务器时,他们会发送请求,从而建立连接。在第一个请求中,状态为“NEW”,但此后的所有请求都具有“ESTABLISHED”或“RELATED”(与现有连接;辅助连接)状态。当然,这在其他协议上可能会有所不同,但 HTTP/HTTPS 并没有那么花哨。

    我们将采取平衡的做法,采取以下措施:

  • 作为服务器 — 允许新的和已建立的入站 80,443 流量发往我们的 Web 服务器,并且仅允许到已建立连接的传出数据包。
  • 作为客户端 — 允许发往外部 Web 服务器(apt 使用、外部 API)的新的和已建立的出站 80,443 流量,并且仅允许已建立的入站连接。
  • SSH (22) — 仅允许新的或已建立的入站流量和专门已建立的出站连接。这有效地阻止了我们的服务器建立自己的 SSH 连接。
  • ICMP(协议)和 DNS(通过 UDP 为 53)— 允许受限 ICMP 和 ESTABLISHED 入站 DNS、NEW、ESTABLISHED 出站 DNS。由于这不是 DNS 服务器,因此我们不接受新的或不相关的入站 DNS 数据包。
  • 并删除不符合我们规则的所有内容。
  • 此设置提供了坚实的基础并限制了潜在的暴露面,同时允许相当自由的标准 HTTP/HTTPS 流量流动。

    理论上,如果您使用 NGINX 作为反向代理,您也可以将此设置与 MERN 堆栈一起使用。由于 Node.js 和 NGINX 之间的流量是内部的,因此它将顺利地通过保持打开的环回接口流动。

    安全注意事项

    一旦你将服务器或机器暴露在互联网上,你就会注意到总会有人或某物在扫描和探测它。要么试图使用流行的不安全凭据闯入,要么扫描开放端口,要么检查你部署的 Web 应用程序是否存在常见的配置错误或环境漏洞。

    需要强调的是,安全不是目标或目的地,而是指导我们行动的指令。你无法实现安全,但你可以尽力减轻恶意活动。未来需要记住的另一件事是:**通过隐蔽性实现的安全不是真正的安全**。这是一种有缺陷的安全原则,它注重保密而不是实际保护。

    在某些情况下,ICMP 可能被恶意用作隧道方法。我们将通过阻止除入站“echo-r​​equest”类型消息和传出“echo-r​​eply, destination-unreachable”消息之外的所有 ICMP 来缓解这种情况。这将允许外部机器 ping 我们(对于基本健康检查很有用),但阻止我们的服务器发送除基本响应之外的任何 ICMP 消息,并限制 ICMP 隧道攻击的可能性,但不能完全根除它。如果您不打算使用 ICMP,请随意在您的配置中不包含这些规则或稍后将其删除。

    DNS 可以做类似的事情,这就是我们也限制端口“53”UDP 上的流量的原因。

    服务器本身不应该能够在没有任何保护措施的情况下发送传出请求。开放传出通信会增加病毒或代码执行漏洞引发的数据泄露风险。此配置是一个很好的基础,但如果您想更进一步,在安装完所有内容后,您应该删除允许作为客户端进行外部连接的规则,而只允许向特定 IP 地址进行传出客户端通信。我们将在本指南的最后介绍此过程。

    在速率限制方面,我建议不要使用标准的 `iptables` 限制功能,因为这不会区分合法连接和暴力攻击。即使对具有 `NEW` 状态的数据包设置速率限制,也会导致在您尝试在暴力攻击或常规漏洞探测/扫描期间连接的情况下被锁定。您应该使用专门的实用程序,例如 `fail2ban` 来实现此目的。

    **在开始配置之前,我们必须明白,如果攻击者直接获得了服务器的访问权限,我们的防火墙将无法阻止他们。**

    配置

  • 如果你还没有安装 iptables,那么让我们用 apt 安装它: sudo apt install iptables iptables-persistent 在 iptables-persistent 安装过程中,你会被要求保存并加载当前的 IPv4 和 IPv6 iptables 规则。此时,选择保存以确认两个提示。
  • (可选)如果您不想保留以前的规则,并且没有将输入或输出链上的默认操作设置为 DROP 或 REJECT,请删除所有当前规则:危险!这可能会导致您无法访问您的机器!sudo iptables -F
  • 设置入站规则。可以直接粘贴整个块: sudo iptables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT; \ sudo iptables -A INPUT -p tcp --dport 443 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT; \ sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT; \ sudo iptables -A INPUT -p icmp --icmp-type echo-r​​equest -j ACCEPT; \ sudo iptables -A INPUT -p udp --sport 53 -m conntrack --ctstate ESTABLISHED -j ACCEPT 注意 ; 运算符,它允许命令链。有了它,即使前一个命令失败,命令也会执行。最重要的是,我们使用 \ 多行运算符使命令链更清晰。分解一下我们刚才做的事情:-A 标志表示“附加”,这告诉应用程序我们想要在特定链 (INPUT) 的末尾添加此规则。-p 参数表示“协议”,它定义此规则适用于哪种协议。我们的所有端口都在 TCP 上工作,因此我们只允许通过 TCP 传输数据包。--sport 参数可以在 iptables-extensions(8) 手册中找到,它定义允许哪些源(“s”)端口。--dport 参数定义数据包的目标端口。-m 参数表示“匹配”,在我们的例子中,它将数据包的状态匹配为 NEW 或 ESTABLISHED。这将只允许新的或已建立的连接与我们的机器通信。我们正在使用 conntrack 模块,这就是它使用 --ctstate 的原因。 -j 参数表示“跳转”,指定当数据包与我们的规则匹配时要执行的操作。这里我们想接受所有内容。
  • 设置出站规则: sudo iptables -A OUTPUT -p tcp --sport 80 -m conntrack --ctstate ESTABLISHED -j ACCEPT; \ sudo iptables -A OUTPUT -p tcp --sport 443 -m conntrack --ctstate ESTABLISHED -j ACCEPT; \ sudo iptables -A OUTPUT -p tcp --sport 22 -m conntrack --ctstate ESTABLISHED -j ACCEPT; \ sudo iptables -A OUTPUT -p icmp --icmp-type echo-r​​eply -j ACCEPT; \ sudo iptables -A OUTPUT -p icmp --icmp-type destination-unreachable -j ACCEPT; \ sudo iptables -A OUTPUT -p udp --dport 53 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
  • 设置允许作为客户端进行外部连接的规则: sudo iptables -A INPUT -p tcp --sport 80 -m conntrack --ctstate ESTABLISHED -j ACCEPT; \ sudo iptables -A INPUT -p tcp --sport 443 -m conntrack --ctstate ESTABLISHED -j ACCEPT; \ sudo iptables -A OUTPUT -p tcp --dport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT; \ sudo iptables -A OUTPUT -p tcp --dport 443 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT 您会注意到这些规则交换了源端口、目标端口和连接状态。这是因为当我们连接到远程服务器时,我们的机器充当客户端,因此与 Apache 的方式相反。
  • 允许内部(环回)流量:sudo iptables -A INPUT -i lo -j ACCEPT; \ sudo iptables -A OUTPUT -o lo -j ACCEPT 如果没有这些规则,您的数据库将无法与您的 Web 应用程序通信,因为我们还在后面设置了通用 DROP 规则。
  • 在设置默认 DROP 规则之前,请检查您的配置是否包含预期的规则: sudo iptables -L -vn 参数如下: -L 参数表示“列表”,它列出所选链中的所有规则。如果未指定链,则将显示所有链。 -v 参数表示“详细”,这提供了有关每个规则的更多详细信息。 -n 参数表示“数字”,这将阻止 iptables 尝试解析主机名并以数字形式显示 IP 地址和端口。输出应如下所示:
  • 将默认行为更改为丢弃所有其他数据包:危险!这可能会导致您无法访问您的计算机! sudo iptables -P INPUT DROP; \ sudo iptables -P OUTPUT DROP; \ sudo iptables -P FORWARD DROP 这将丢弃所有不符合我们之前的规则的流量,并且是有效防火墙的关键组件。我们自然希望丢弃所有 FORWARD 链数据包,因为这基本上是一个资源提供者而不是路由器。在任何情况下都不应该进行转发,如果发生类似的事情,则很可能存在一些恶意活动,例如隧道。
  • 保存配置:sudo netfilter-persistent save 这将首先将我们刚刚设置的规则保存到文件中并更新配置。
  • 防止不需要的传出 HTTP/HTTPS

    按照服务器本身不应该在自身上建立新连接(充当客户端)的想法,我们应该禁用潜在的不安全规则来锁定我们的服务器,同时允许其提供网络内容所需的最低限度。

    如前所述,如果您在服务器端调用任何外部 API,即在您的 PHP 代码中(或在服务器端呈现的 JavaScript 应用程序中),则必须保留这些规则以避免出现连接问题。删除它们还将导致所有将外部数据拉入您计算机的软件包无法运行,最重要的是“apt/apt-get”和“curl”。

    幸运的是,至少从管理角度来说,还是有解决方案的。我个人知道的有:

  • 端口敲击 — 这依赖于向服务器发送一系列“敲击”请求,之后服务器将:仅为发出敲击的 IP 地址打开一个端口(SSH)并设置允许客户端 HTTP/HTTPS 请求的规则。您可以从 cs.fyi 上的《端口敲击实用指南》文章中了解更多信息。
  • 脚本 — 编写一个脚本,在您登录后附加允许客户端 HTTP/HTTPS 规则,并在您完成任务后删除它们。这可以自动完成,但也可以手动完成。
  • 在所有情况下都会有一些开销。

    使用此代码片段删除客户端规则:

    sudo iptables -D INPUT -p tcp --sport 80 -m conntrack --ctstate ESTABLISHED -j ACCEPT; \
    sudo iptables -D INPUT -p tcp --sport 443 -m conntrack --ctstate ESTABLISHED -j ACCEPT; \
    sudo iptables -D OUTPUT -p tcp --dport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT; \
    sudo iptables -D OUTPUT -p tcp --dport 443 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

    当你需要它们时,可以使用此代码片段将它们放回去:

    sudo iptables -A INPUT -p tcp --sport 80 -m conntrack --ctstate ESTABLISHED -j ACCEPT; \
    sudo iptables -A INPUT -p tcp --sport 443 -m conntrack --ctstate ESTABLISHED -j ACCEPT; \
    sudo iptables -A OUTPUT -p tcp --dport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT; \
    sudo iptables -A OUTPUT -p tcp --dport 443 -m conntrack --ctstate NEW,ESTABLISHED -j

    **恭喜!您已提高服务器的安全性。**

  • 您已通过实施强大的防火墙强化了系统。
  • 您已经获得了防火墙工作原理的基本知识。
  • 您还深入了解了一般和特定的安全问题(例如隧道、泄露、速率限制)
  • 发现错误?请告诉我!

    感谢您的阅读。希望本指南能提供一些价值 :)