- NFQUEUE 允许 Netfilter 将过滤和标记决策委托给用户空间进程,从而实现动态 IP 防火墙和路由器。
- Suricata 提供了一个多进程 IDS/IPS 引擎,支持 NFQUEUE、AF_PACKET 以及与 Snort 和新兴威胁兼容的规则。
- 将 NFQUEUE 与 Suricata、数据库、Memcached 或 Pfsense 集成,即可使用免费软件构建高级安全和路由解决方案。
- 性能很大程度上取决于线程设计和用户空间逻辑,因此优化和仔细选择要检查的流量至关重要。
如果您在 GNU/Linux(安全性和隐私性最佳的 Linux 发行版)上从事网络工作,并且对超越传统的静态防火墙感兴趣,那么您可能很想知道如何将 Netfilter、NFQUEUE 和 Suricata 结合起来,构建一个真正灵活的入侵检测/防御系统 (IDS/IPS),而无需在专有硬件上花费巨资。这正是我们将在本文中探讨的内容,我们将底层元素(内核、队列、C 语言)与高级工具(Suricata、规则、MySQL、Memcached、pfSense)融合在一起。
其核心思想非常强大:利用内核(参见如何优化 Linux 内核)可以将数据包排队到用户空间,并由自定义程序决定如何处理这些数据包。这可以用于流量过滤(高级防火墙、入侵防御系统)、动态路由或集成业务逻辑(数据库、缓存、Web 应用程序攻击检测、VoIP 等)。如果我们再添加 Suricata 作为多进程入侵检测/防御系统引擎,就能得到一个非常强大的组合,适用于从实验室到高流量数据中心的各种环境。
NFQUEUE 和 Netfilter:将防火墙提升到用户空间
在典型的 GNU/Linux 系统中,Netfilter/iptables(或 nftables)规则通常作为静态策略,完全驻留在内核空间。前端和设备(包括许多基于 Netfilter 或 BSD Packet Filter 的解决方案)将配置存储在文本、XML 或 SQLite 文件中,并在配置更改时重新生成并加载规则。这种方式虽然灵活,但其逻辑仍然只是防火墙的一种快照,仅进行一些细微的动态调整(例如每秒连接数限制、连接跟踪、国家/地区匹配、是否启用第 7 层等)。
NFQUEUE 的方案可谓颠覆性的:它不再总是由内核做出最终决定,而是将决策权委托给用户进程。内核将数据包放入一个编号队列,而使用 libnetfilter_queue 库的应用程序则从队列中取出数据包,进行分析,并返回一个判断结果:接受、丢弃,甚至将其标记为策略路由。这就像在防火墙之上增加了一个用 C、Python 或 Perl 编写的可编程“法官”。
这样做的好处在于,我们的程序可以随心所欲地执行任何操作:在响应内核之前,查询 /dev/urandom、数据库、Web 服务、分布式缓存或复杂的算法。从架构角度来看,防火墙不再是一组简单的静态规则,而变成了一个管道,其中 Netfilter、队列和用户应用程序像拼图一样完美契合。
NFQUEUE 由两部分组成:iptables 中的 NFQUEUE 目标,用于将数据包发送到特定队列;以及libnetfilter_queue 用户库,用于读取这些数据包并做出判断。它并非像 tcpdump 那样简单的嗅探器:在这里,我们可以直接决定数据包的传输路径。
使用 NFQUEUE 进行基本的 iptables 配置
从 iptables 的角度来看,使用 NFQUEUE 非常简单:您只需在感兴趣的链中添加一条规则,即可将符合特定条件(源/目标 IP、端口、状态、额外的模块,例如 GeoIP 或 layer7(如果可用)等)的数据包发送到队列。
例如,如果我们想将到达主机本身的所有 ping 请求发送到 NFQUEUE:
iptables -I INPUT -p icmp -j NFQUEUE
这会将传入的 ICMP 数据包发送到队列 0(除非另有指定)。我们可以使用类似`--queue-num 3` 的参数指定另一个队列。使用计数器列出规则(`iptables -L -n -v -x`)时,我们会看到计数器递增,表明数据包正在排队。一个重要的细节是:如果队列中有数据包,但没有用户进程检索和处理它们,则默认行为是拒绝这些数据包,因此,应用程序故障会导致流量被阻止,这是设计使然。
使用 libnetfilter_queue 进行编程:C 语言的“Hello World”程序
要从用户空间加入队列,需要使用libnetfilter_queue库(该库又依赖于 libnfnetlink)。在 Debian 等发行版上,只需安装开发软件包即可:
apt-get install libnfnetlink-dev libnetfilter-queue-dev gcc
一个始终接收数据包的最小程序框架由几个非常清晰的步骤组成:打开库,解除所有现有处理程序的绑定,绑定到 AF_INET 协议,创建带有回调函数的队列,定义复制模式,然后进入接收循环。回调函数会针对每个入队的数据包执行,提取数据包 ID 并返回处理结果。
实际上,流程大致如下:`nfq_open` 获取句柄,`nfq_unbind_pf` 清理句柄,`nfq_bind_pf` 与 AF_INET 关联,`nfq_create_queue` 在队列 0 中注册回调函数,`nfq_set_mode` 指示需要元数据还是整个数据包,循环使用 `recv()` 遍历描述符,`nfq_handle_packet` 处理每个数据包。退出时,使用 `nfq_destroy_queue` 销毁队列,并使用 `nfq_close` 关闭句柄。
这种“Hello World”示例可以清晰地衡量NFQUEUE的影响。如果我们用类似这样的代码编译示例:
gcc -o nftest code.c -lnfnetlink -lnetfilter_queue
如果我们对来自 iperf 的流量进行排队(例如,输入和输出都使用 TCP 端口 5001),我们会发现,仅仅接收数据包的代码对千兆网络的性能影响甚微。然而,这里有一个关键细节:在回调函数中打印信息(例如 printf、fflush 等)会显著降低吞吐量,这一点可以通过比较启用和禁用屏幕调试的 iperf 测试结果来验证。
NFQUEUE 高级选项:旁路、平衡和故障开启
NFQUEUE 包含几个有趣的 iptables 选项,这些选项会修改队列的默认行为,在进入生产或高性能环境之前应该了解这些选项,因为它们会影响用户应用程序故障或队列填充的处理方式。
`--queue-bypass`命令可以确保在没有进程监听队列时,数据包不会被丢弃,而是转发到 iptables 链中的下一跳。如果您希望系统在用户服务不可用时“故障保持开放”,这将非常有用,但从安全角度来看,这把双刃剑也会带来风险。
`--queue-balance`选项允许您将数据包分配到一系列队列(例如,从 0 到 3),然后让多个独立的进程或线程分别从每个队列中消费数据包。Netfilter 的代码确保来自同一数据流的数据包始终进入同一个队列,这大大简化了决策逻辑的一致性维护。
此外,还有`--fail-open`模式,它控制着当用户进程运行过慢导致队列填满时会发生什么。启用此模式后,内核会直接接收数据包而不是丢弃它们,从而防止大规模流量中断。同样,这可能存在安全隐患,因为如果我们想要根据具体情况做出决策,丢失决策数据包就意味着无法实现该目标。
为了监控队列的运行情况,Netfilter 在伪文件系统/proc/net/netfilter/nfnetlink_queue中公开了相关信息,可以通过脚本或监控工具轻松查询。
集成业务逻辑:使用 Memcached 和 MySQL 进行测试
一旦“Hello World”流程运行正常,下一步自然就是通过调用外部系统来丰富回调函数。一个典型的实验是,根据源IP地址是否出现在任何后端(例如MySQL数据库或Memcached缓存)中来决定是否接受或拒绝数据包。
对于 Memcached,首先需要安装守护进程(使用命令 `apt-get install memcached`),然后加载一个密钥(例如`authorized`),其中包含我们感兴趣的 IP 地址。我们可以使用简单的 `echo` 和 `netcat` 命令来完成此操作,然后使用 `get` 命令验证该值是否已正确存储。接下来,NFQUEUE 程序除了获取数据包 ID 外,还会使用`NFQNL_COPY_PACKET`接收整个数据包,提取 IP 头部(`struct iphdr`),并使用 `inet_ntop` 将源地址转换为字符串。
为了避免每次数据包都建立连接而浪费时间,Memcached 连接仅在主方法(memcached_create、memcached_server_list_append、memcached_server_push)中初始化一次,并将处理程序存储在全局变量中。在回调函数中,使用所需的键调用 memcached_get 函数,将源 IP 地址与检索到的值进行比较,如果匹配,则返回 NF_ACCEPT;否则,返回 NF_DROP。如果键不存在或出现错误,则出于保守策略,丢弃该数据包。
使用 iperf 测试发现,这种策略在千兆网络上的吞吐量会降低到大约 140 Mbit/s,并且观察到队列开始出现丢包(例如,代码本身中的符号就表明了这一点)。换句话说,即使每个数据包都调用缓存服务,也会产生相当大的开销,尽管如果进行优化,它在中等流量的情况下仍然可行。
使用 MySQL 的方法类似,但更复杂:首先安装服务器和客户端库,然后创建一个数据库(例如 nfqueue),其中包含一个名为 authorized(ip varchar(50)) 的简单表,并将允许的 IP 地址插入其中。程序启动时会运行`mysql_init` 和 `mysql_real_connect`,并在回调函数中构造一个类似 `select * from authorized where ip like 'xxxx'` 的查询。如果查询成功执行并找到匹配的行,则接受该包;否则,丢弃该包。
启用 MySQL 查询缓存后,测试结果约为188 Mbit/s ;禁用查询缓存后,速度降至103 Mbit/s 。虽然这些数据远未达到千兆级别,但足以证明,即使采用最不理想的方法(单线程、未优化),基于数据库或缓存的决策也能处理可观的流量。
性能、多线程和 CPU 使用率
使用 iperf、Memcached 和 MySQL 进行的测试清楚地表明,性能瓶颈并非主要由 NFQUEUE 本身决定,而是由我们在用户空间添加的逻辑及其实现方式决定。一个仅返回 NF_ACCEPT 的可执行文件可以轻松达到近千兆的吞吐量;一旦引入 I/O 或网络调用,吞吐量就会下降,NFQUEUE 执行服务器、Memcached 守护进程或 MySQL 的 CPU 就会达到极限。
从架构角度来看,这具有两层含义。一方面,它证实了在考虑每次调用的实际成本的情况下,将防火墙决策委托给用户应用程序来处理大量流量是完全可行的。另一方面,它表明,为了充分发挥平台的性能,必须考虑多线程或多进程。NFQUEUE 允许将流量分配到多个队列中;我们可以启动应用程序的多个副本,每个副本监听不同的队列,并利用多核处理器,而无需使用 pthreads 或进行大规模的进程分叉。
另一个显而易见的优化方法是限制哪些流量会通过 NFQUEUE。在测试中,整个 iperf 流都被排队,但在实际场景中,我们可以只对状态为 NEW 的数据包进行排队,允许状态为 ESTABLISHED/RELATED 的数据包通过,并将耗时的逻辑留给登录或可疑模式的处理。
归根结底,CPU 使用率和线程设计是关键:如果用户进程不足,队列就会填满,我们不得不采取诸如故障打开或接受丢弃之类的措施,从而失去这种方法旨在实现的一些精细控制。
Netfilter品牌动态路由
NFQUEUE 不仅限于简单地“接受”或“丢弃”。它还可以用于将 Netfilter(fwmark)标志应用于数据包,并将其与 ip 规则和 iproute2 结合使用,以创建高度灵活、几乎轻量级的 VRF 式策略路由方案。
大致来说,该过程如下:在/etc/iproute2/rt_tables中定义几个路由表,例如 slow 和 fast;为每个表分配不同的默认路由(一个通过光纤,另一个通过更有限的链路);使用 ip 规则指定 fwmark 为 1 的数据包进入 fast 表,fwmark 为 2 的数据包进入 slow 表,依此类推;最后,使用 NFQUEUE 在返回结果之前对数据包进行适当的标记。
要从回调函数中设置判决结果,可以使用`nfq_set_verdict2` 函数,它与 `nfq_set_verdict` 类似,但允许您设置一个判决值,该值随后会被 `ip rule` 函数识别。结合所有这些,您可以构建一个 IP 路由器,该路由器可以根据任意标准决定路由方向:从奇偶数据包大小这种看似荒谬的标准,到流量预测算法、社交媒体事件或监控系统信号等外部输入。
其结果是,内核继续以通常的速率转发数据包,但每个数据流的具体路径都委托给外部软件,该软件可以实时改变其想法,而无需触及静态规则。
NFQUEUE 和 Suricata:GNU/Linux 中的高级 IPS
以上所有功能都可以用 C 语言手动编写,但对于入侵检测和深度包检测而言,更明智的选择通常是依赖成熟的 IDS/IPS 引擎。Suricata 正是为此而生,它最初就是作为 Snort 的多进程替代方案而诞生的,从一开始就具备 IPS 功能,并着重利用当今可用的众多 CPU 核心。
Suricata 完全从零开始编写,并以GPLv2 许可证发布;开放信息安全基金会 (OISF) 负责维护引擎以及相当全面的规则和文档生态系统。与继承了单线程核心并在此基础上进行修改的 Snort 2.x 不同,Suricata 的设计理念是将工作负载分配到多个线程上:捕获、解码、检测和输出,并采用不同的负载均衡策略。
在功能层面上,Suricata 提供对IPv6 的原生支持、第 7 层检测(通过 HTP 库实现非常高级的 HTTP)、与端口无关的协议识别、流重建,以及一个非常强大的会话变量(流位)系统,用于关联跨多个 TCP 连接的攻击的不同阶段。
另一个优势是它与 Snort 规则的兼容性,以及它能够同时使用 Sourcefire VRT 和 Emerging Threats 签名集(免费的 ET Open 版本和商业版 ET Pro 版本)。此外,它还能以非常实用的格式(fast.log、eve.json 格式的 JSON)导出事件,以便与 SIEM、ELK、Splunk 和其他系统集成。
Suricata 作为 Linux 中的 IPS:捕获模式和 NFQUEUE
在 GNU/Linux 系统上,Suricata 可以根据流量拦截方式的不同以不同的模式运行:NFQUEUE、AF_PACKET、PF_RING、libpcap、NFLOG、IPFW、DAG、Napatech等。每种模式都有其自身的优势和要求。在纯粹的入侵防御系统 (IPS) 层面,最重要的两种模式是 NFQUEUE 和 AF_PACKET。
在NFQ(NFQUEUE)模式下,流程与前面描述的类似:一组 iptables 规则将数据包发送到队列;运行在用户空间的 Suricata 从该队列读取数据包,根据其规则检查数据包内容,并将结果返回给内核:NF_ACCEPT、NF_DROP 或 NF_REPEAT。第三种方法(NF_REPEAT)可用于在应用其他标记或修改后,将数据包重新注入到同一个 iptables 表中。
这种模式非常灵活,易于在现有基础设施中实现,因为它只需要修改特定节点(例如,FORWARD、INPUT、OUTPUT)的规则,其他部分保持不变。其代价是需要通过 NFQUEUE 上下传递数据包,如果数据包数量非常大或规则资源消耗较大,则会产生上述影响。
在AF_PACKET模式下,Suricata 更靠近网络接口运行,通过 AF_PACKET 套接字复制数据包。这是一种速度更快的零拷贝方法,但它要求系统作为具有两个接口的网关运行,并且流量阻塞必须在网卡之间的转发层执行:要阻塞的数据包根本不会从输入接口传递到输出接口。
在两种模式下,Suricata 都可以与 Netfilter 结合使用,但NFQUEUE 特别适合我们想要重用所有 iptables 逻辑(策略、范围、先前规则)并且只将我们有兴趣深入检查的流量发送到 Suricata 的场景。
从源代码安装 Suricata 的基本步骤
对于那些喜欢编译 Suricata 而不是使用软件包的人来说,在 Debian/Ubuntu 类型的发行版上,该过程包括首先安装编译依赖项(build-essential、libpcre、libpcap-dev、libnet-dev、libyaml-dev、zlib、libcap-ng-dev、libjansson-dev 等),从官方网站下载 tarball,然后运行经典的 ./configure、make、make install。
在配置阶段,脚本会指示已启用哪些支持功能:AF_PACKET 是/否、PF_RING、NFQUEUE 是/否、NFLOG、IPFW、对 libnss、libjansson、Prelude、PCRE JIT、Lua、GeoIP 等的支持。如果我们想在该模式下工作,则必须验证 NFQUEUE 是否已启用,并且我们感兴趣的捕获库已找到。
安装二进制文件后,您可以运行`make install-conf`将默认配置部署到 `/etc/suricata`,并运行`make install-rules`下载并向 `/etc/suricata/rules` 中放置一组新兴威胁规则。之后,可以使用 `suricata-update` 等工具更新这些规则集。
在 Red Hat/CentOS 系统上,逻辑类似,使用 yum 或 dnf 安装依赖项(libpcap-devel、pcre-devel、libnet-devel、libyaml-devel、jansson-devel 等),然后按照相同的步骤进行编译。出于性能考虑,建议使用 ethtool在捕获接口中禁用 LRO/GRO,因为这些卸载功能可能会影响 IDS 层对软件包的可见性。
Suricata 配置:YAML、变量和线程
Suricata 的主要配置文件位于/etc/suricata/suricata.yaml。这是一个可读性较高且注释详尽的 YAML 文件,其中定义了从日志路径和规则集到目标操作系统策略和线程参数等所有内容。
其中一个基本字段是`default-log-dir`,它指定日志文件的存储位置(默认为 `/var/log/suricata`)。`vars` 部分包含诸如 `HOME_NET`、`EXTERNAL_NET`、`HTTP_PORTS`、`SHELLCODE_PORTS` 和 `SSH_PORTS` 之类的变量,它们在规则中用作缩写。`HOME_NET` 通常配置为我们想要保护的本地网络范围,而 `EXTERNAL_NET` 通常定义为 `!HOME_NET`。
另一个重要组成部分是主机操作系统策略 (host-os-policy),它告诉 Suricata 哪些操作系统应该运行特定的 IP 地址范围。这使得 Suricata可以调整其 TCP 重组装方式或对某些网络协议栈行为的解释,从而更难利用协议栈之间的差异(例如 Windows 与 Linux)来规避协议。可以将特定的 IP 地址范围分配给 Windows、Linux、BSD、Vista、Windows 2003 等类别。
关于线程设置,线程部分允许您微调 CPU 亲和性和检测线程数。默认情况下,`set-cpu-affinity` 通常处于禁用状态,这使得系统调度器能够将线程分配到各个核心上。`detect -thread-ratio`参数指示每个可用核心创建的检测线程数;例如,在 8 核机器上,如果将 `detect-thread-ratio` 设置为 1.5,Suricata 将生成 12 个检测线程,外加捕获线程和管理线程。
整个模型会在守护进程启动时反映在输出中:除了流管理器和统计管理器之外,还可以看到捕获线程(例如 pcap)和多个检测器线程。这种多进程架构使得 Suricata在面对 10/40 Gbit/s 链路时,比单线程引擎具有更好的扩展性。
Suricata 中的规则和签名更新
Suricata 依靠规则集来检测攻击模式、异常行为和协议滥用。除了接受Snort 格式的规则外,最常见的生态系统是 Emerging Threats:ET Open(免费)和 ET Pro(商业),其规则针对当前威胁。
许多现代发行版都包含`suricata-update`工具,它可以简化规则管理:更新源、启用或禁用特定提供程序,以及下载最新版本的签名集。典型的工作流程是:安装 `suricata-update`(例如,通过 pip),运行第一个 `suricata-update` 以下载 ET Open,使用 `suricata-update list-sources` 列出源,启用其他源,例如 `ptresearch/attackdetection`、`oisf/trafficid` 或 `sslbl/ssl-fp-blacklist`,然后再次运行 `suricata-update` 以重新生成规则文件。
suricata.yaml 文件已进行调整,指向正确的规则路径。之后,Suricata 将开始触发警报事件,这些事件将被记录在fast.log(快速、易读的文本文件)和 eve.json(包含非常完整信息的结构化 JSON 文件)中。后一种格式尤其适用于为仪表盘、关联系统或自定义脚本提供数据。
除了签名之外,Suricata 还集成了多种协议的解码器和解析器,使其对端口的依赖性降低:即使 HTTP 流量通过非标准端口,它也能识别它;它还能检测不同端口和封装级别(包括混合 IPv4/IPv6 隧道)上的 SSH、TLS、DNS 等。
实际应用:从检测网络漏洞到自动拦截
在托管环境或数据中心中,最理想的用例之一是实时检测利用 Web 应用程序(例如 WordPress 及其插件)漏洞的尝试,并自动做出反应,通常是通过在防火墙中阻止或将源 IP 列入黑名单。
Suricata 凭借更新后的规则,能够识别针对 URL、参数、HTTP 有效负载甚至与已知漏洞匹配的请求序列的特定攻击模式。该入侵检测系统 (IDS) 可以被动运行,通过交换机端口镜像 (SPAN) 接收流量;但要作为入侵防御系统 (IPS) 并阻止攻击,则需要将其集成到转发平面中。
有两种常见的方法:一种是将入侵检测系统 (IDS) 设置为在线桥接,使流量实际经过该设备(根据平台使用 iptables、AF_PACKET 或 PF),另一种是保持网络拓扑不变,但将镜像与通过 API、脚本或 NFQUEUE 对中央防火墙执行的操作相结合。第一种方法最大限度地减少了检测和拦截之间的延迟,但代价是在网络“中间”增加了一个元素;第二种方法提供了更高的灵活性和弹性,但增加了编排的复杂性。
入侵检测系统 (IDS) 完全可以检测到试图利用存在漏洞的 WordPress 插件的行为,然后直接或通过相关组件将攻击者的 IP 地址添加到 iptables 黑名单中。这可以通过Suricata 的 JSON 输出和调用 iptables/nftables 的脚本来实现,也可以通过将部分逻辑委托给 NFQUEUE 来实现,NFQUEUE 引擎本身或相关进程可以即时做出决策,而无需等待外部列表更新。
这样一来,您就可以专注于真正重要的威胁(漏洞利用、权限提升尝试、非常激进的扫描),而忽略或仅仅记录背景噪音,例如基本的端口扫描,这些扫描在很多情况下本身并不令人担忧。
PfSense 上的 Suricata:集成了 IDS/IPS 的开源防火墙
并非每个人都能或愿意购买像 Palo Alto 这样的高端专有防火墙。在许多环境下,使用 pfSense 和 Suricata 搭建开源解决方案更具吸引力,它既能满足高级防火墙需求(多 WAN、VLAN、VPN、NAT 等),又能提供 IDS/IPS 功能。
基于 FreeBSD 和数据包过滤器的 Pfsense 在虚拟化环境(Proxmox、KVM 等)中表现尤为出色,但有一点例外:如果想要避免在高负载下出现性能问题和崩溃,建议在 KVM 机器中使用 E1000 网卡而不是 Virtio 网卡,除非您采纳 Netgate 的建议(在“系统”>“高级”>“网络”中禁用硬件校验和卸载并重新启动,但请注意,在高负载下,这可能还不够)。
在 Pfsense 上运行 Suricata 的实验室的最低硬件要求可能比较低(1 个 500 MHz 的 CPU、1 GB 内存、4 GB 磁盘),但对于严肃的使用,建议至少使用 2 个 CPU、4 GB 内存和 16 GB 存储空间,不要忘记配备多个网络接口(一个用于 WAN,另一个用于 LAN,如果需要多个 WAN 或复杂的 VLAN,则需要更多)。
安装 pfSense 本身非常快捷:从 ISO 镜像启动,接受许可协议,选择安装,选择语言和键盘布局,分区设置为自动(如果要使用整个磁盘,则选择自动 UFS),几分钟后系统即可启动。控制台提供了一个菜单,用于分配接口、重启、启动 shell 等。
例如在实验室的 VirtualBox 中,通常会使用 pfctl -d 从控制台暂时禁用 Pfsense 防火墙,以便通过 WAN 访问 Web 界面(用户名 admin,密码 pfsense),并完成初始向导:常规数据、NTP 服务器、WAN 配置(在实验室中 DHCP 通常就足够了)、LAN、更改管理员密码和应用配置。
一旦访问稳定,您可以在 WAN 防火墙中创建一条规则,允许任何来源的 HTTPS 流量访问 pfSense 的 IP 地址,并添加描述性分隔符以方便地组织规则(例如,“防火墙访问”)。如果您在测试环境中使用 RFC1918 地址,建议禁用 WAN 上阻止专用网络的选项,以避免频繁使用 `pfctl -d` 命令。
在 pfSense 上安装 Suricata 及概述
pfSense 基础系统启动并运行后,安装 Suricata 非常简单:只需依次进入“系统”>“软件包管理器”>“可用软件包”,搜索 Suricata 并安装即可。该过程会下载多个文件,耗时可能因硬件配置而异,但整个过程都会通过 Web 界面提供全程协助。
安装完成后,Suricata 的条目会出现在“服务”选项卡中,您可以在其中按接口(WAN、LAN、VLAN 等)配置实例,选择要使用的规则集,激活 IDS 或 IPS 模式,以及调整性能和日志记录参数。选项非常丰富(仅配置部分就足以写成几篇文章),但其优势在于,许多在 Linux 中需要手动编辑 YAML 文件的任务,在这里都可以通过表单和复选框轻松完成。
重要提示:虽然在实验室环境中直接将 pfSense 管理界面暴露在互联网上可能很诱人,但在生产环境中,必须限制对静态 IP 地址的访问,使用 VPN 进行远程管理,并务必避免 Web 控制台暴露在外。pfSense 非常灵活,但同时也必须将其视为至关重要的组件。
在 pfSense 上启用 Suricata 后,网络流量会先经过 pfSense 进行防火墙和 NAT 处理,然后由Suricata 根据其规则进行检查,并在 IPS 模式下阻止流量。这种组合通过单一的 Web 界面进行管理,极大地简化了在中小型网络中部署 DPI 防护的过程。
在许多部署中,Pfsense/Suricata 与 SIEM 或集中式日志平台连接起来,利用结构化的输出格式来关联事件并检测更广泛的攻击活动,从而起到补充作用。
Suricata 中的事件监控和示例日志
Suricata 运行后,事件会被记录到默认日志目录定义的路径中,通常是/var/log/suricata。fast.log文件使用紧凑的文本格式,包含时间戳、规则 ID、分类和优先级,适合从终端快速查看(tail -f)。
例如,当遇到 TCP 校验和错误的流量时,我们可能会看到类似这样的信息行:包含日期和时间的时间戳,后跟规则标识符(例如 1:2200074:1)、消息“SURICATA TCPv4 校验和无效”、分类、优先级以及源-目标 IP/端口对。这类警报有助于快速识别数据包完整性问题或规避尝试。
eve.json 文件包含 JSON 格式的相同事件,字段包括 timestamp、event_type、src_ip、dest_ip、src_port、dest_port 和 proto,以及一个包含 action、gid、signature_id、rev、signature、category 和 severity 等信息的告警子文件。这种格式可以轻松地被 Logstash、Fluentd、Filebeat 或任何其他日志代理读取,从而实现比纯文本更丰富的分析功能。
在多核服务器(例如 8 核)上部署 Suricata 时,线程压缩在 htop 等工具的线程模式下非常明显,会显示一个或多个捕获线程(pcap、AF_PACKET 或 NFQ)以及大量分布在各个核心上的检测线程。当流量接近平台极限时,调整检测线程比例和 CPU 亲和性可以显著影响吞吐量和延迟。
在部署到生产环境之前,建议花些时间微调已激活的规则集,以避免出现大量误报,从而阻止合法流量或使日志变得混乱。Suricata-update 允许您禁用整个类别或单个规则,以便在灵敏度和可用性之间找到合理的平衡点。
特殊应用:VoIP、音频分析和创意 NFQUEUE
除了经典用途(Web 服务保护、恶意软件检测、DDoS 分析)之外,Netfilter + NFQUEUE 组合还能在 VoIP 等领域实现极具创意的解决方案。例如,可以设置反 SPIT(IP 电话垃圾邮件)过滤器,或者用于审查RTP 流中不雅词汇的系统。
其思路是:通过端口或协议识别来识别 RTP 流量,并将其发送到 NFQUEUE;从用户应用程序,使用 librtp 等库重建 RTP 流,提取 WAV 格式的音频,并将其传递给关键字识别引擎(词点识别),例如第三方提供的合成或识别库。
根据检测到的词语,NFQUEUE 进程可以决定允许、阻止甚至通过在音频流中插入哔声来改变播放,尽管后者需要对 RTCP、数据包序列和时间进行非常精细的控制——几乎是一种中间人攻击。这并非易事,但理论上可以通过利用相同的队列和判定系统来实现。
确实,其中一些操作可以通过简单的嗅探器将数据传输到外部处理器,然后对 SIP 信令或通过 SBC(例如 Asterisk、Kamailio 等)进行处理来实现。使用 NFQUEUE 的区别在于,它可以立即直接地对 RTP 流进行操作,无需协调多个组件或等待信令层完成呼叫。
这些场景清楚地说明了 GNU/Linux + Netfilter + Suricata + 第三方库组合的潜力:它不仅仅是阻止端口和 IP 地址,而是使用 100% 自由软件生态系统实时协调复杂的流量决策。
从始终接受数据包的小型 C 程序到集成了 NFQUEUE、Pfsense、数据库和缓存的多进程 Suricata 部署,纵观整个发展历程,人们可以体会到该技术栈的灵活性,它能够构建从简单的动态防火墙到数据中心规模的 IDS/IPS 架构,具备真正的深度检测能力,并能自动响应日益复杂的攻击。