Hotdry.
systems

Netfence:面向 eBPF 过滤器的控制平面架构设计

解析 Netfence 如何通过控制平面与守护进程分离的架构,实现 eBPF 过滤器的动态策略热更新,涵盖 TC/cgroup 挂载、DNS 解析联动与 gRPC 同步机制。

传统的 eBPF 包过滤方案面临一个根本性的工程困境:过滤策略通常以静态方式编译进 eBPF 程序或通过配置文件预定义,当业务需要临时放行某个域名、更新 IP 网段或调整黑白名单模式时,必须重新加载 eBPF 程序甚至重启守护进程。这种设计在需要快速响应安全事件或实现动态访问控制的场景中显得力不从心。Netfence 的核心设计理念正是解决这一痛点,它将策略决策从内核 eBPF 程序中剥离出来,放到用户态的控制平面中完成,再通过 gRPC 双向流将策略变更实时同步到运行在各个主机上的守护进程,最终由守护进程负责更新内核中已挂载的 eBPF 过滤程序。整个架构借鉴了 Envoy xDS 的设计哲学,但将应用场景从七层代理下沉到四层包过滤,形成了一套完整的动态策略热更新机制。

架构解耦:控制平面与数据平面的职责划分

Netfence 采用三层架构模型,每个层级承担明确的职责并通过精心设计的接口进行通信。最顶层是用户自主实现的控制平面,它只负责策略的存储、计算和下发,不关心 eBPF 程序的加载细节或网络接口的挂载状态。中间层是部署在每个虚拟机或容器主机上的守护进程 netfenced,它运行在用户空间,负责管理 eBPF 程序的完整生命周期,包括编译、加载、挂载、更新和卸载。最底层是内核中的 eBPF 过滤程序,它们以 TC classifier 或 cgroup skb 的形式存在,真正执行数据包的拦截或放行动作。这种职责分离带来的最大好处是控制平面可以使用任何语言实现,只要遵循 gRPC 协议即可,而守护进程专注于 eBPF 生态系统的复杂性,对外暴露简洁的本地 API。

从数据流向来看,策略变更的传播路径是控制平面发起 SetMode、AllowCIDR、AllowDomain 等 gRPC 消息,守护进程接收后更新其内存中的策略状态,然后将更新后的规则写入 eBPF map。eBPF 程序在每次包处理时查询这些 map 做出转发决策,整个过程无需重新加载 eBPF 程序。值得注意的是,守护进程在启动时会主动建立与控制平面的双向流连接,控制平面通过 SyncRequest 请求当前所有已挂载的附件信息,守护进程则通过 SyncAck 响应并报告自身状态。此后控制平面可以随时推送新的策略,守护进程也会主动上报附件的增删和心跳统计信息。这种设计保证了在网络抖动或短暂断开连接时,双方能够通过重同步机制恢复到一致状态,避免了策略漂移的风险。

eBPF 过滤程序的两类挂载方式

Netfence 支持两种不同的 eBPF 程序挂载方式,分别针对不同的网络隔离场景进行了优化。第一种是 TC classifier 挂载,适用于虚拟机或物理机环境中的 veth 虚拟网卡和物理以太网接口。TC classifier 是 Linux 流量控制子系统的核心组件,eBPF 程序可以挂载到 qdisc 的 ingress 或 egress 点,在数据包经过网络栈的关键位置时触发执行。Netfence 推荐使用 clsact qdisc 作为挂载载体,这种专门为分类器设计的 qdisc 不会引入额外的队列管理逻辑,只负责调用绑定的 eBPF 程序。eBPF 程序返回 TC_ACT_OK 表示放行数据包,返回 TC_ACT_SHOT 表示丢弃,没有任何中间状态保证了过滤决策的原子性。

第二种挂载方式是 cgroup skb,适用于容器化环境中对单个 cgroup 进行网络隔离。每个容器在 Linux 中都对应一个 cgroup 目录,cgroup skb 程序可以挂载到 cgroup 的 ingress 或 egress hook,对该 cgroup 内所有进程产生的网络流量进行过滤。与 TC classifier 相比,cgroup skb 的优势在于与容器生命周期的天然绑定,容器创建时挂载过滤程序,容器销毁时自动卸载,无需额外的清理逻辑。但 cgroup skb 也有其局限性,它只能看到进程的套接字层信息,无法访问数据包的物理接口信息,因此在需要基于物理接口做策略分流的场景中,TC classifier 是更合适的选择。Netfence 的设计允许用户根据实际需求混合使用两种挂载方式,例如对宿主机上的虚拟机使用 TC classifier,对容器使用 cgroup skb,统一由同一个守护进程管理。

DNS 解析联动与 IP 自动填充机制

Netfence 的另一个创新设计是将 DNS 解析与 IP 过滤规则进行深度联动,实现基于域名的动态访问控制。在传统的包过滤方案中,如果策略要求仅允许访问特定域名,管理员需要事先解析出该域名的所有 IP 地址并手动维护这个列表,这不仅繁琐而且容易因 DNS 缓存失效或负载均衡变更导致策略失效。Netfence 的做法是为每个附件分配一个独立的本地 DNS 服务器端口,当容器或虚拟机向该端口发起 DNS 查询时,守护进程会拦截查询并检查域名是否在允许名单中。如果域名匹配且 DNS 模式为 allowlist,守护进程会执行真实的 DNS 解析并将响应返回给客户端,同时将解析得到的 IP 地址自动添加到 eBPF IP 过滤 map 中。此后从该附件发出的流量只要目的 IP 在允许列表中就会被放行,无需额外的 DNS 查询开销。

这种设计的工程价值在于它将域名策略转换为 IP 策略的过程自动化了,管理员只需要在控制平面配置域名规则,IP 列表的维护完全由系统自动完成。域名规则支持子域名匹配和 specificity 优先级,例如同时配置 allowlist api.example.com 和 allowlist *.example.com 时,更具体的 api.example.com 规则优先级更高。IP 过滤 map 支持 IPv4 和 IPv6 CIDR 格式,可以配置 TTL 来自动过期临时解析的 IP 地址,防止因域名解析记录长期缓存导致的策略僵化。控制平面还可以选择将 DNS 查询代理到自身来做更细粒度的决策,例如根据附件的元数据信息对同一个域名返回不同的解析结果,实现多租户场景下的域名级隔离。

生产环境部署的关键配置参数

在生产环境中部署 Netfence 需要关注多个配置维度的权衡,这些参数直接影响系统的可用性、性能和运维复杂度。首先是控制平面同步超时参数 control_plane.subscribe_ack_timeout,守护进程在收到 Subscribed 事件后会阻塞等待控制平面的 SubscribedAck,默认超时时间为 5 秒。这个超时时间的设置需要平衡两方面因素:超时过短可能导致控制平面负载高峰时大量附件初始化失败,超时过长则会拖慢容器启动速度或增加故障恢复时间。建议根据控制平面的实际处理能力和业务容器的启动 SLA 动态调整这个值,对于无状态工作负载可以设置为 2-3 秒,对于有复杂初始化逻辑的 StatefulSet 可以适当延长到 10 秒左右。

其次是 IP 地址的 TTL 配置,Netfence 允许为每条 AllowCIDR 规则设置生存时间,过期后自动从 eBPF map 中移除。这个功能主要用于应对域名解析结果变化或临时放行场景,但 TTL 的设置需要与 DNS 缓存策略协调。如果域名的 TTL 是 300 秒而 Netfence 的 IP TTL 设置为 600 秒,在 DNS 记录变更后旧 IP 仍会被放行 300 秒;如果 IP TTL 设置过短比如 60 秒,则每次 DNS 响应后都要频繁更新 eBPF map 增加 CPU 开销。建议将 IP TTL 设置为 DNS TTL 的 1.5 到 2 倍作为默认行为,重要服务可以配置为与 DNS TTL 相同以减少窗口期风险。最后是附件元数据的命名规范,Netfence 支持在 Attach 时传入 key-value 格式的元数据用于控制平面策略决策,常见的元数据包括 vm_id、tenant、env、region 等,建议使用统一的前缀命名约定并避免在元数据值中包含敏感信息。


参考资料

查看归档