跳转到帖子

游客您好,欢迎来到黑客世界论坛!您可以在这里进行注册。

赤队小组-代号1949(原CHT攻防小组)在这个瞬息万变的网络时代,我们保持初心,创造最好的社区来共同交流网络技术。您可以在论坛获取黑客攻防技巧与知识,您也可以加入我们的Telegram交流群 共同实时探讨交流。论坛禁止各种广告,请注册用户查看我们的使用与隐私策略,谢谢您的配合。小组成员可以获取论坛隐藏内容!

TheHackerWorld官方

牧云插件系统技术选型之插件通信框架大PK

精选回复

发布于

本文将探讨基于WASM 的牧云主机安全系统插件系统开发中通信方式的选择。我们将从性能、安全性、易用性等方面比较不同通信的优缺点,并解释我们最终选择的原因和哪种通信解决方案。这是“Host Agent插件引擎开发故事”系列文章的第六篇,以后会持续更新。本系列文章将带领您深入了解长汀牧云团队的主机Agent插件引擎的开发过程。内容涵盖了技术选型、插件接口设计、组件通信框架等多个方面,并详细讲解了其背后的原理和实现方法。无论您是网络安全专业人士,还是对技术开发感兴趣的读者,您都可以从中有所收获。我们希望通过分享开发过程中面临的挑战、解决方案和实践经验,提供深入的见解和有价值的技术参考,帮助读者了解如何构建高效可靠的安全产品,共同推动安全技术社区的发展。

## 简介

牧云主机安全系统是基于“WebAssembly”的安全解决方案,旨在提供值得信赖的主机环境,保护用户数据的安全。

插件系统是牧云主机安全系统的核心组件。它允许用户根据具体需求定制功能并添加自己的扩展模块。通过插件系统,用户可以为主机环境添加新的安全策略、监控和审计功能,以满足不同应用场景的需求。

插件系统开发的目标是提供一个灵活、安全、高性能的插件框架,使用户能够轻松地开发和集成插件。为了实现灵活高效的插件系统,开发团队需要选择合适的通信方式和RPC框架。

## 插件系统通信要求

### 涉及的通信类型

插件系统涉及的通信类型包括以下几个方面:

1.插件与宿主程序之间的通信:

插件需要与宿主程序进行交互,比如获取系统信息、调用宿主程序提供的功能接口等。

2.插件执行器之间的通信:

插件执行者可能需要进行通信和协调工作,例如传输数据、共享资源、实现任务分发等。

3、插件执行器与核心引擎之间的通信:

核心引擎为插件提供其安全功能所需的安全事件源。插件执行器需要与核心引擎进行交互,比如获取引擎状态信息、发送指令、接收引擎事件通知等。

4、核心引擎与管理器之间的通信:

Manager为Agent程序提供全面的管理功能。核心引擎需要与管理器通信,传达系统状态、接收管理器的指令、代理‘IO’请求等。

5、管理器为其他组件提供的状态维护和协同控制信号:

管理器需要向其他组件提供状态维护和协同控制的信号,例如传递配置信息、启动/停止插件执行器等。

6、管理器与服务平台之间的数据上报、配置下发等通知信号:

管理器需要与服务平台进行通信,例如向平台上报数据、接收配置信息、发送通知信号等。

这些通信需求涵盖了插件系统内各个组件之间的交互和协作。为了保证插件能够有效地与宿主环境、执行器、引擎和管理器进行通信,达到功能扩展、资源共享和协同工作的目标,必须选择合适的通信方法和协议,保证通信的高效、安全和可靠。

### 插件通信框架中的消息特征

在牧云插件系统中,需要处理复杂的数据结构和具有特定特征的通信。以下是沟通的一些主要特征:

1.数据结构复杂度:

- 通信中需要传输的数据结构通常比较复杂,包含多层嵌套和各种数据类型。这些数据结构可能包括对象、数组、枚举等,并且需要高效的序列化和反序列化操作。

2.大量短消息和大消息:

- 通信过程中主要存在大量的短消息(即主机安全系统需要处理的各种安全事件),但也会出现一些较大的单条消息(包含更多的上下文信息)。因此,通信框架需要能够高效处理大量小消息,并支持较大消息的传输和处理。

3、异步通信:

- 通信必须异步运行,以提高系统的响应能力和并发处理能力。否则,将无法高效应对大量安全事件。

4.数据类型模型的差异:

- 宿主`Agent`程序的不同组件涉及大量的混合语言编程,不同的编程语言有不同的数据类型模型。因此,通信框架需要能够处理不同数据类型之间的转换和兼容,以保证数据的正确传输和解析。

以上这些都是在选择通信框架时需要考虑的,并确保所选择的框架能够满足这些需求。

## 通讯方式选择

说到跨组件通信,大多数人可能首先想到的是各种`RPC`,然后就是各种网络通信。不过,这里我们想说的是,首先,没有必要使用`RPC`,其次,最好不要使用网络堆栈。考虑一个技术问题的最佳选择,首先要从业务需求出发,其次也是最重要的是从底层技术出发**。

### 底层通信方式的优缺点

下面列出了不同底层通信方式的优缺点:

从底层技术角度来看,值得考虑的通信方式包括:

-FFI

- ABI 稳定

- 共享内存

-动态库加载

-IPC

- Unix域套接字

- Unix 上的管道/先进先出

- Windows 上的命名管道

- STDIO

- MMAP

-网络

-TCP

接下来我们一一分析一下这些通信方式的特点和适用场景:

1.FFI(外部功能接口):

- ABI Stable: 使用稳定的应用程序二进制接口,允许不同编程语言之间直接进行函数调用。适用于需要高性能、低延迟的通信场景,但调试相对困难,组件之间耦合度高,容易出现接口变更、版本变更带来的兼容性问题。对于Rust 来说,由于Rust 本身的ABI 接口还不稳定,需要通过C ABI 接口进行通信。

2.共享内存:

- 使用共享内存实现进程间通信,可以实现高效的数据传输和共享。适用于需要高性能、低延迟,但需要正确处理并发控制和同步问题的本地通信场景。很容易出现竞争条件和数据不一致,从而难以实现正确的并发控制。

3.动态库加载:

- 加载动态库并调用其中的函数实现通信。它具有较高的灵活性,适合需要动态扩展和灵活配置的通信场景,但很容易导致运行时错误。需要保证加载的动态库版本和接口的一致性,还需要处理动态库的生命周期管理。

4.进程间通信(IPC):

- `Unix Domain Socket`: 在`Unix-like`系统中提供了一种高效、简单、直观、稳定的本地进程间通信方法。

- `Pipe`/`FIFO`: 在`Unix`系统上,使用管道或`FIFO`(命名管道)提供了一种简单的进程间通信方式,但仅支持单向字节流。适合具有共享继承关系的进程间通信场景。

- `Named Pipe` : 在`Windows`系统中,一种叫做命名管道的技术更为强大,提供了一种简单的、全双工的通信方式,并且可以通过网络通信支持跨机器的进程间通信。

- `STDIO`: 使用标准输入输出流进行进程间通信是一种简单的方式,全平台都支持,但它是同步阻塞`IO`,速度相对较慢,只适合父子进程通信场景。

5.内存映射(MMAP):

- 使用内存映射技术将文件映射到进程的地址空间。如果同一个文件映射在一起,就可以在进程之间共享数据。但很难实现正确的并发读写和同步控制,而且很容易暴露敏感数据。适用于需要高效数据读写和同步控制的通信场景。

6.网络通讯(Network):

- `TCP`: 使用传输控制协议进行网络通信。跨平台、跨网络的通信方式成熟稳定,但速度相对较慢。

在选择通信方式时,需要根据具体的业务需求和底层技术进行权衡。需要考虑通信性能要求、跨平台跨网络要求、安全性要求以及灵活性和易用性等因素。

另外,还需要考虑通信协议的支持和适应性,保证通信方式与插件系统上层应用层协议相匹配,实现数据流式传输、流压缩、安全加密等功能。

### 选择原则

选择通信方式时,通常可以考虑以下原则:

1、性能和效率:所选的通信方式应具有较高的性能和效率,能够满足系统的实时性和吞吐量要求。

2、安全稳定:所选的通信方式应具有一定的安全保障,能够保障通信过程中数据传输和访问的安全,同时还能提供稳定性,减少通信故障的发生。

3.可扩展性和灵活性:所选的通信方式应具有良好的可扩展性,能够适应系统需求变化和功能扩展,具有高度灵活性,易于开发人员使用和维护。

4、跨平台、跨机器的支持:所选的通信方式应该能够在不同操作系统和机器之间进行通信,提供良好的跨平台和分布式环境支持。

5、开发和维护成本:选择通信方式时,需要考虑开发和维护的成本,包括学习成本、调试和故障排除成本以及后续的扩展和维护成本。

6、社区支持和生态系统:选择交流方式时,可以考虑是否有活跃的社区支持和完整的生态系统,可以提供开发文档、示例代码、解决方案等资源。

7、上层应用兼容性:选择通信方式时,需要考虑上层应用的兼容性,保证通信方式能够匹配上层应用的协议和接口,实现数据的正确传输和分析。

具体到主机安全场景,我们还需要特别关注简单性、高效性、异步性、易于正确实现、支持复杂通信、需要同时考虑不同平台等。

## 选择结果

根据前面提到的选型原则,我们的选型结果如下:

1.本地通讯:

- 在“Linux”下,我们选择使用“Unix Domain Socket”作为本地通信方式。它高效、稳定,并且仅限于本地使用。

- 在“Windows”下,我们选择使用“命名管道”作为本地通信方法。它支持全双工通信。

2、跨机通信:

- 我们选择基于“TCP”的方法作为跨机器通信方法。 `TCP`具有跨平台、稳定的优点,适合分布式环境。

## 应用层通信协议选择

合适的底层通信方式为后续的通信协议和上层应用提供了可靠的基础。但基于底层通信机制,还需要更方便的上层通信协议来提供包括流压缩、复用、安全加密等功能。值得考虑的通信方式包括:

1. **基于TLS 的HTTP 2.0 上的OpenAPI**

- 优点:标准化的RESTful API描述方式,支持流压缩、安全加密等特性。

- 限制:性能可能略低于其他选项。

2. **基于TLS 的HTTP 2.0 上的gRPC**

- 优点:高效的数据传输机制,支持流式传输、复用和安全加密。

- 限制:对于本地通信来说可能过于复杂和繁重。

3. **基于TLS 的WebSocket**

- 优点:支持全双工通信,适合实时或需要频繁交互的应用场景。

- 限制:与“Raw WebSocket”相比,可能会增加一些计算开销。

4. **基于Unix 域套接字的原始WebSocket/Windows 上的命名管道**

- 优点:支持全双工通信,与`Unix Domain Socket`或`Named Pipe`结合,无需网络堆栈的参与,减少通信开销,并且无需加密即可提高性能。通过结合“Raw WebSocket”和“OS”级别的“IPC”机制提供了良好的平衡。

- 局限性:通信安全性较弱,仅限于本地通信。

对于主机`Agent`,我们希望本地通信一样高效、多通道、异步、不需要考虑加密;而远程通信除了效率之外还需要考虑稳定性、跨平台、安全性。

最后,对于本地通信,我们选择在“Linux”系统上使用“Raw WebSocket over Unix Domain Socket”,在“Windows”系统上使用“Raw WebSocket over Named Pipe”,以实现高效、多路复用、异步通信;对于远程通信,我们选择使用“gRPC on HTTP 2.0 over TLS”来提供跨语言交互性和稳定性。

##业务通信逻辑模型

在上层的具体业务中,仅有应用层通信协议是不够的。我们还需要在应用层通信协议的支持下,采用更贴近业务的通信模型来管理组件之间的通信。具体来说,这里我们采用中心辅助点对点总线通信模型。每个组件都与注册中心通信,以了解它需要与之通信的另一个组件的地址和状态。通信过程实际上是点对点的,但逻辑上是通过总线的统一模型。0637c9a0cecf424c790633b89cea669c.png

## RPC 框架

有了基本的沟通模型,我们还需要具体的沟通方式。这里我们需要选择一个`RPC`框架。考虑以下方法:

- [gRPC](https://github.com/hyperium/tonic)

- [tarpc](https://github.com/google/tarpc)

- [JSON-RPC](https://github.com/paritytech/jsonrpsee)

- 自行实现的[bincode-rpc](#bincode-rpc)

`gRPC`/`tarpc` 以及所有其他为微服务设计的`RPC` 协议对于插件本地环境访问来说过于复杂和重量级。

`JSON-RPC`是无状态的,结构简单,所以比较合适。然而,**JSON 的序列化和反序列化速度太慢/内存消耗太高/数据类型模型与Rust 差异较大**。您可以使用bincode 来替换JSON-RPC 中的序列化方法。

tarpc 是由Rust 实现的RPC 协议,它使用bincode 作为序列化方法。然而,“tarpc”是针对微服务场景设计的,具有**不必要的开销**,不适合直接采用。

综上所述,基于`JSON-RPC`协议设计并实现一个`bincode-rpc`是比较合适的。

### bincode-rpc

考虑到使用场景是‘频繁小数据’+‘原生可靠通信’,我们需要一个极其轻量/快速/简单/安全的RPC框架。任何现有的RPC框架都会带来太多不必要的消耗。因此,需要自己实现`bincode-rpc`框架,设计基本遵循[JSON-RPC 2.0 SPEC](http://wiki.geekdream.com/Specification/json-rpc_2.0.html)。

`bincode-rpc` 的主要功能包括:

- 纯‘Rust’实现,性能可控,与探针语言一致

- 不需要单独的`proto`文件,`schema`直接在代码中定义

- 使用非常简单,‘注重易用性’

- 支持基于`tokio`的异步

- 支持可配置的超时控制/日志跟踪

- 与具体通信方式无关,只要通道支持流式传输即可

这是一个简单的`bincode-rpc`结构示例,包括请求结构和响应结构:

请求结构示例:

```json

{\'bincoderpc\': 1, \'方法\': \'减法\', \'params\': [42, 23], \'id\': 1}

````

``生锈

使用bincode:{配置,解码,编码};

#[推导(编码、解码、PartialEq、调试)]

结构BincodeRpcRequest {

bincoderpc: u8,

method: 字符串,

params: Vec,

id: u32,

}

````

响应结构示例:

```json

{\'bincoderpc\': 1,\'结果\': 19,\'id\': 1}

````

``生锈

使用bincode:{配置,解码,编码};

#[推导(编码、解码、PartialEq、调试)]

结构BincodeRpcResponse {

bincoderpc: u8,

结果: Vec,

id: u32,

}

````

说明:

- `bincoderpc` 字段用于标识协议版本号,固定为`u8` 类型的值。

- `method` 字段表示远程调用的方法名称,类型为`String`。

- params 字段是传递给方法的参数,类型为Vec,是实际参数结构体的序列化结果。

- ‘id’字段用于标识请求和响应之间的对应关系,类型为‘u32’。

- `result`字段表示方法的返回结果,类型为`Vec`,是实际响应结果结构体的序列化结果。

- 不支持通知机制。

- 不支持批量调用。

bincode-rpc框架可以根据这样的结构体定义进行序列化和反序列化,从而实现请求和响应的传输和解析。

## 结论

通过本文的讨论,我们讨论了牧云主机安全系统插件系统开发中的通信需求和技术选型。在选择通信方式时,我们考虑了底层技术的优缺点,选择了合适的方式,即“Unix Domain Socket”、“Named Pipe”和“TCP”。针对不同的沟通需求,我们选择合适的沟通方式。

在应用层通信协议的选择上,我们考虑了“OpenAPI”、“gRPC”、“WebSocket”和“Raw WebSocket”等选项,并根据需要选择了合适的协议。

关于RPC框架的选择,我们综合考虑了现有框架的优缺点,最终决定自己实现“bincode-rpc”框架。 bincode-rpc 具有纯Rust 实现、性能可控、易用性强、支持异步的特点。适合频繁的小数据和可靠的本地通信的需求。

通过以上选择,我们为基于‘WASM’的牧云主机安全系统插件系统开发提供了高效、安全、可靠的通信解决方案。插件系统可以实现与宿主程序、执行器、引擎、管理器的无缝通信和协同工作,满足用户的定制需求,提高系统的灵活性和功能扩展性。

## 推荐相关博文

上一篇:**【牧云插件系统技术选型:插件开发用什么? 】](https://stack.chaitin.com/techblog/detail?id=85)**

系列文章目录:【【预览】主机Agent插件引擎开发故事总结】(https://stack.chaitin.com/techblog/detail?id=82)

1. **[[揭秘牧云插件开发者的创新之路:从无法解决的问题到“充满乐趣”]](https://stack.chaitin.com/techblog/detail?id=72)**

2. **[【牧云插件系统面向未来的设计原则]](https://stack.chaitin.com/techblog/detail?id=73)**

3. **【【牧云插件系统选型之争】】(https://stack.chaitin.com/techblog/detail?id=77)**

4. **【【牧云插件系统技术选型中探针开发用什么? 】](https://stack.chaitin.com/techblog/detail?id=83)**

5. **【【牧云插件系统技术选型及插件开发? 】](https://stack.chaitin.com/techblog/detail?id=85)**

6. **【【牧云插件系统技术选型插件通信框架PK】】(https://stack.chaitin.com/techblog/detail?id=94)**

7. **[【牧云插件系统技术选型:大家都会抱怨的序列化方法选择之争]](https://stack.chaitin.com/techblog/detail?id=97)**

8. **[【牧云插件系统技术选型:应该选择哪种WASM运行时?如果不选择这个,就会出大问题! 】](https://stack.chaitin.com/techblog/detail?id=105)**

创建帐户或登录后发表意见

最近浏览 0

  • 没有会员查看此页面。