Post

Suricata protol change(ai)

Suricata protol change(ai)

Suricata 协议变更机制 (ChangeProto) 与 NFS 加密方法

本文档旨在详细解释 Suricata 中应用层协议变更的机制,并探讨 NFS 协议未采用类似机制的原因,以及当前 NFS 流量的加密方法。

第一部分:Suricata 的协议变更机制 (AppLayerRequestProtocolChange)

在 Suricata 中,当一个 TCP 连接上的应用层协议从一种类型动态切换到另一种类型时,会触发一个“协议变更”流程。用户提到的 suricataChangeProto 实际上对应于 Suricata 内部的 AppLayerRequestProtocolChange 函数及其相关逻辑。

1.1 核心概念

  • FLOW_CHANGE_PROTO 标志: 这是在 Flow 结构体中设置的一个位标志,用于指示某个流需要进行协议变更。
  • FlowChangeProto(Flow *f): 一个辅助函数,用于检查 FLOW_CHANGE_PROTO 标志是否已设置。
  • FlowSetChangeProtoFlag(Flow *f): 设置 FLOW_CHANGE_PROTO 标志。
  • AppLayerRequestProtocolChange(Flow *f, uint16_t dp, AppProto expect_proto): 这是高级 API,用于请求应用层协议变更。它会设置 FLOW_CHANGE_PROTO 标志,并记录期望的新协议 (f->alproto_expect) 和原始协议 (f->alproto_orig)。
  • SCAppLayerRequestProtocolTLSUpgrade(Flow *f): AppLayerRequestProtocolChange 的一个封装函数,专门用于请求升级到 TLS 协议,通常将 expect_proto 设为 ALPROTO_TLS

1.2 触发协议变更的场景

协议变更机制主要用于以下三类场景:

1.2.1 STARTTLS / SSL加密升级 (明文 -> TLS)

当明文协议通过特定的命令协商升级到加密传输(TLS)时触发。调用的是 SCAppLayerRequestProtocolTLSUpgrade,它会将预期协议设置为 ALPROTO_TLS

  • SMTP (邮件传输):
    • 当客户端发送 STARTTLS 命令,且服务器返回 220 (Service ready) 响应时。
  • FTP (文件传输):
    • 当客户端发送 AUTH TLS 命令,且服务器返回 234 响应时。
  • POP3 (邮件接收):
    • 当客户端发送 STLS 命令,且服务器返回 OK 状态时。
  • LDAP (目录服务):
    • 当客户端发送 StartTLS 的扩展请求(Extended Request),且服务器返回成功结果 (ResultCode(0)) 时。
  • PostgreSQL (数据库):
    • 当客户端发送 SSLRequest,且服务器返回 S (SSLAccepted) 字节时。

1.2.2 HTTP 协议升级 (HTTP/1.1 -> 其他协议)

当 HTTP/1.1 通过 Upgrade 头部字段协商升级到更高效或全双工的协议时触发。

  • HTTP/2:
    • 当检测到 Upgrade: h2c 头部,且服务器同意升级时,协议切换为 ALPROTO_HTTP2
  • WebSocket:
    • 当检测到 Upgrade: WebSocket 头部,且服务器同意升级时,协议切换为 ALPROTO_WEBSOCKET

1.2.3 HTTP 隧道 (HTTP CONNECT)

  • HTTP Proxy Tunneling:
    • 当客户端发送 CONNECT 方法(通常用于通过代理建立 HTTPS 连接),且服务器返回 2xx 成功状态码时。
    • 处理方式: 此时会将协议重置为 ALPROTO_UNKNOWN(未知),并指示 Suricata 基于 CONNECT 请求中的目标端口重新进行协议检测(通常会检测出内部封装的 TLS 流量)。

1.3 协议变更的详细流程 (以 SMTP STARTTLS 为例)

Suricata 的协议变更流程是一个复杂的状态机转换过程,涉及 解析触发标记变更流状态重置协议重检测 四个阶段。

假设客户端(Client)正在向服务器(Server)发送邮件,初始使用明文 SMTP 协议。

1.3.1 触发阶段 (Detection Trigger)

  • 动作: 客户端发送 STARTTLS 命令,服务器回复 220 2.0.0 Ready to start TLS
  • 代码位置: src/app-layer-smtp.c
  • 逻辑:
    1. SMTP 解析器解析到状态码 220,且当前上下文是 STARTTLS 命令的响应。
    2. 调用 SCAppLayerRequestProtocolTLSUpgrade(f)

1.3.2 标记变更 (Flagging)

  • 代码位置: src/app-layer-detect-proto.c -> AppLayerRequestProtocolChange
  • 逻辑:
    1. 设置流标志 FLOW_CHANGE_PROTO
    2. 记录预期的新协议 f->alproto_expect = ALPROTO_TLS
    3. 保存当前协议 f->alproto_orig = f->alproto (即 ALPROTO_SMTP)。

1.3.3 流状态重置 (Flow Reset)

  • 动作: 处理完当前的 220 响应包后,Suricata 的主工作线程需要清理旧状态。
  • 代码位置: src/flow-worker.c
  • 逻辑:
    1. 检查 FlowChangeProto(p->flow) 是否为真。
    2. 强制刷新日志: 调用 StreamTcpDetectLogFlush,确保之前的 SMTP 事务日志被记录(如 eve.json 中的 smtp 记录)。
    3. 关闭旧解析器: 设置 APP_LAYER_PARSER_EOF_TS/TC 标志,通知 SMTP 解析器“连接结束”(尽管 TCP 连接还在,但在逻辑上 SMTP 阶段已结束)。

1.3.4 协议重检测 (Re-detection)

  • 动作: 客户端发送下一个包(即 TLS ClientHello 握手包)。
  • 代码位置: src/app-layer.c
  • 逻辑:
    1. 数据包进入应用层处理函数 (AppLayerHandleTCPData 或类似函数)。
    2. 检测到 FlowChangeProto(f) 标志仍然存在。
    3. 重置检测状态: 调用 AppLayerProtoDetectReset(f),将流的当前协议 f->alproto 重置为 UNKNOWN
    4. 执行检测: 调用 TCPProtoDetect(...)
      • 检测逻辑会优先检查 f->alproto_expect (此时是 ALPROTO_TLS)。
      • TLS 探测器分析 ClientHello 包,匹配成功。
      • f->alproto 被更新为 ALPROTO_TLS
    5. 清理旧状态:
      • 调用 FlowUnsetChangeProtoFlag(f) 清除变更标志。
      • 调用 AppLayerParserStateProtoCleanup(...) 释放旧的 SMTP 解析器内存。
    6. 新协议启动: 随后调用 AppLayerParserParse,此时它会为 TLS 协议分配新的解析器状态,并开始解析加密流量。

这个流程实现了“无缝热切换”:旧协议正常解析直到切换点,中间状态触发日志刷新和状态冻结,新数据到来时利用预设的期望值快速完成新协议识别,新协议接管后续数据流,旧协议资源被回收。

第二部分:NFS 协议没有协议变更机制的原因

NFS (Network File System) 目前在 Suricata 中没有协议变更流程,主要原因如下:

2.1 协议本身的交互机制不同

Suricata 的 ChangeProto 机制是为了处理 “在同一个 TCP 连接中,协议类型发生根本性改变” 的场景。NFS 的传统工作模式不符合这一特征:

  • SMTP/FTP/HTTP 等协议通常以“明文控制协议”开始,通过特定命令(如 STARTTLS, Upgrade)协商后,整个连接切换为另一种协议(TLS 或 HTTP/2)。Suricata 需要把解析权从旧协议解析器移交给新协议解析器。
  • NFS (v3/v4) 建立连接后,主要一直在传输基于 RPC (Remote Procedure Call) 的 NFS 数据。在标准的 NFS 协议中,没有“先用 NFS 聊几句,然后切换成 HTTP 或其他完全不同的协议”这种机制。它的应用层协议在连接的生命周期内通常是固定的。

2.2 NFS over TLS 的现状

虽然较新的标准(如 RFC 9289 定义了 RPC-over-TLS)允许 NFS 通过类似 STARTTLS 的方式升级到加密连接,但 Suricata 目前的代码尚未实现对这一特性的检测和处理逻辑

  • rust/src/nfs 源码目录下,没有任何调用 AppLayerRequestProtocolChangeSCAppLayerRequestProtocolTLSUpgrade 的代码。
  • NFS over TLS 开启后,Suricata 将无法看到内部的文件操作内容。

2.3 端口协商 vs 协议切换

NFS 经常涉及 Portmapper (rpcbind),但这属于 动态端口检测,而不是协议切换:

  • 客户端先连接 Portmapper (通常 TCP 111),查询 NFS 服务所在的端口。一旦获取到信息,客户端会直接连接到该 NFS 服务端口。
  • 这是通过 AppLayerProtoDetect 模块处理的。当 Suricata 在新的端口上看到流量时,会进行全新的协议检测,识别出那是 NFS。这并不是在同一个连接上把“协议 A”变成“协议 B”,而是两个独立的连接的识别过程。

总结

NFS 没有 ChangeProto 流程,是因为传统 NFS 协议没有这种动态切换协议的机制,且Suricata 尚未实现对最新的 NFS over TLS 标准的检测和处理

第三部分:NFS 开启加密的方法

当前在生产环境中,NFS 开启加密主要有两种方式:通过 RPCSEC_GSS (Kerberos) 或通过 NFS over TLS,以及更通用的 网络层加密。不同方法对 Suricata 的可见性影响不同。

3.1 RPCSEC_GSS (Kerberos)

这是 NFS 长期以来支持的认证和加密机制,通过 GSS-API (Generic Security Services Application Programming Interface) 实现。

  • 配置方式: 通常在客户端挂载时指定 sec=krb5 (认证), sec=krb5i (认证+完整性), 或 sec=krb5p (认证+完整性+隐私/加密)。
  • 工作原理:
    • 认证 (krb5): 使用 Kerberos 票据进行客户端和服务器的身份验证。数据负载仍是明文。
    • 完整性 (krb5i): 在 krb5 的基础上,对每个数据包计算加密校验和 (checksum)。这能防止数据被篡改,但数据负载本身仍是明文。
    • 隐私/加密 (krb5p): 在 krb5i 的基础上,使用 Kerberos 密钥对整个数据负载进行加密。这是真正的“NFS 加密”,可防止窃听。

Suricata 对 RPCSEC_GSS 的支持与可见性:

  • Suricata 的 NFS 解析器(rust/src/nfs)能够识别 RPCSEC_GSS 认证类型(Auth Flavor 6)。
  • 对于 krb5krb5i 模式: Suricata 能够解析 RPCSEC_GSS 的头部,并剥离出内部的 NFS 操作内容。因此,即使开启了完整性保护,Suricata 仍然可以检测 NFS 的文件操作(如 GETATTR, READ, WRITE 等)。
  • 对于 krb5p 模式: 由于数据负载被完全加密,Suricata 无法看到内部的 NFS 操作内容,只能识别到这是加密的 NFS 流量。

3.2 NFS over TLS (RFC 9289)

这是较新的标准,旨在通过标准的 TLS 协议为 NFS 提供传输层加密,类似于 HTTPS。

  • 工作原理: 客户端与服务器建立 TCP 连接后,通过 RPC 请求中的特殊机制(类似于 STARTTLS)协商并启动 TLS 握手。一旦 TLS 握手完成,所有后续的 NFS 数据都会通过 TLS 加密传输。
  • Suricata 可见性: Suricata 目前不支持这种带内协议升级的检测和解密。这意味着一旦 TLS 握手完成并开始加密传输,Suricata 将无法看到 NFS 的具体操作内容。

3.3 VPN / Tunnel (IPsec/WireGuard/OpenVPN)

这是独立于 NFS 协议本身的加密方法,通过在网络层建立加密隧道来实现。

  • 工作原理: 在 NFS 流量发送到网络之前,整个 IP 包会被 VPN 客户端加密,并通过加密隧道传输。只有在隧道两端解密后,原始的 NFS 流量才能被看到。
  • Suricata 可见性: 如果 Suricata 部署在 VPN 隧道之外(即在加密流量经过的地方),它无法看到任何原始的 NFS 内容。只有将 Suricata 部署在 VPN 隧道的末端(即解密后的流量经过的地方),它才能对 NFS 流量进行检测。

3.4 总结:各种加密方法对 Suricata 可见性的影响

加密方法配置关键字原理简述Suricata 可见性
RPCSEC_GSS (krb5)sec=krb5Kerberos 认证,数据明文完全可见
RPCSEC_GSS (krb5i)sec=krb5i认证 + 完整性校验,数据明文可见 (可以剥离头部)
RPCSEC_GSS (krb5p)sec=krb5p认证 + 全加密不可见 (加密负载)
NFS over TLSxprtsec=tls传输层 TLS 加密不可见 (TLS 握手后)
VPN / Tunnel(如 IPsec)网络层加密隧道部署在解密处可见,否则不可见

如果你需要在加密 NFS 流量的同时,Suricata 仍能对其进行深入检测,目前来看,唯一的选择是使用 sec=krb5sec=krb5i 模式,并接受数据负载仍是明文(krb5i 仅提供完整性校验,不提供隐私)。一旦使用 krb5p 或 NFS over TLS 进行全加密,Suricata 的应用层检测能力将受限,无法看到加密内容。

This post is licensed under CC BY 4.0 by the author.