发布于周五 14:565天前 本文将讨论基于WASM 的牧云插件系统开发中插件开发语言的选择。我们会比较不同语言在性能、安全性、易用性等方面的优缺点,并解释我们最终选择哪种语言作为插件开发语言。 \r \r 这是“Host Agent插件引擎开发故事”系列文章的第五篇,以后会持续更新。本系列文章将带领您深入了解长汀牧云团队的主机Agent插件引擎的开发过程。内容涵盖了技术选型、插件接口设计、组件通信框架等多个方面,并详细讲解了其背后的原理和实现方法。无论您是网络安全专业人士,还是对技术开发感兴趣的读者,您都可以从中有所收获。我们希望通过分享开发过程中面临的挑战、解决方案和实践经验,提供深入的见解和有价值的技术参考,帮助读者了解如何构建高效可靠的安全产品,共同推动安全技术社区的发展。 \r \r ## 牧云插件系统后台\r 牧云插件系统是一个基于WebAssembly的高性能、跨平台、强隔离的主机安全系统。在设计和开发过程中,选择合适的插件开发语言对于保证插件的性能、安全性和易用性至关重要。选择合适的编程语言可以有效提高开发效率,降低维护成本,并且支持多种语言以适应未来的开发。因此,在开发牧云插件系统时,我们需要深入探索和权衡各种编程语言的优缺点,找到最适合我们需求的解决方案。 \r \r 综上所述,主机安全插件需要高性能、跨平台、强隔离、强类型、多语言、面向未来。这些就是“WebAssembly”的优点。 \r \r ## WebAssembly\r “WebAssembly”,顾名思义,是一种“Web”汇编语言,也是一种可移植的编译格式。 “WebAssembly”旨在提供更小的文件大小、更快的加载速度、接近本机的执行速度,同时提供高度的隔离性和安全性,旨在成为高级语言的编译目标。目前,“WebAssembly”已广泛应用于云功能、Serverless、客户端跨平台、容器技术等领域。我们正在将其带入网络安全领域。 \r \r 与多种语言可以编译成`JVM` 字节码类似,目前`C`、`C++`、`Rust`、`Go`、`Java`、`C#` 等很多语言都可以编译成`WebAssembly` 字节码。 \r \r ### WebAssembly 数据类型\r WebAssembly 本身仅支持四种数据类型s32、s64、u32、u64(即32 位整数、64 位整数、32 位浮点和64 位浮点)用于模块(`Guest`)和主机(`Host`)之间的通信。传统的解决方案是手动传递指针,并通过反射等方式指定如何使用指针指向的主机内存,以实现对复杂类型的支持。 \r \r (即使是字符串也被认为是复杂类型,只能通过这种方式间接使用。由此可见什么是一个`Low-Level` 计算模型`WebAssembly`。) \r \r ### 使用WebAssembly 插件引擎做什么\r 现在摆在我们面前的是两个困难的选择题:\r \r 第一个是,有这么多语言可供选择,该选择哪一种。 \r \r 第二个就是,沟通这么不方便,用什么方法来沟通。 \r \r ## 插件开发语言的选择\r 关于第一个问题,[Awesome WebAssembly Languages](https://github.com/appcypher/awesome-wasm-langs)给出了一个选择的方向。与“Awesome”类的所有其他“Repo”一样,它包含可以编译为“WebAssembly”或在“WebAssembly”之上拥有自己的“VM”的语言的完整列表。 \r \r 列出的已达到“稳定”级别的语言包括以下语言(不包括那些稳定但不实用的语言,例如“Brainfuck”): \r \r * [.Net](https://github.com/mono/mono/tree/master/sdks/wasm)\r * [C#](https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor)\r * [F#](https://fsbolero.io/)\r * [AssemblyScript](https://github.com/AssemblyScript/assemblescript)\r * [C/C++](https://github.com/emscripten-core/emscripten)\r * [F#](https://hackmd.in.chaitin.net/Lrm8o9J5RFm5GMcwuN9qyA?both#fsharp)\r * [Go](https://github.com/golang/go)\r * [TinyGo](https://github.com/aykevl/tinygo)\r * [Lua](https://github.com/ceifa/wasmoon)\r * [Rust](https://www.rust-lang.org/zh-CN/what/wasm)\r * [Zig](https://ziglang.org/documentation/master/#WebAssembly)\r \r 除了上面提到的“稳定”语言列表之外,值得考虑的语言还包括: \r \r * [QuickJS](https://github.com/bellard/quickjs)\r * [Python](https://github.com/RustPython/RustPython)\r * [Ruby](https://github.com/artichoke/artichoke)\r \r 作为主机安全系统的插件引擎,我们不想要的包括:\r \r * 较大的`开销`,包括虚拟机嵌套等\r * 例如`C#`/`F#`/`QuickJS`/`Lua`/`Python` \r * 更高的迁移成本\r * 不熟悉或困难的语言\r * 例如`C#`/`F#`/`Zig`/`Rust`\r * 不成熟的`WASI` 支持(**不可接受**)\r * 例如`C#`/`F#` \r * 缺乏现代功能(例如模块化、包管理、模式匹配等)\r * 例如`C`/`C++`/`Lua` \r * 缺乏良好的异步支持\r * 例如`C`/`Lua`/`Python` \r * 弱类型或动态类型或渐进类型(**不可接受**)\r * 例如`AssemblyScript`/`QuickJS`/`Lua`/`Python` \r * 可执行文件大小较大(数百MB)\r * 例如`C#`/`F#`/`Python` \r * 内存消耗大(数百MB)\r * 例如`C#`/`F#`/`Python` \r * 较慢的启动时间(秒)\r * 例如`C#`/`F#`/`Python`\r \r 所以我们留下了值得考虑的选项:\r \r * C/C++\r * Go/TinyGo\r * 生锈\r * 锯齿形\r \r 对于`C`/`C++`,在这种使用场景下,我们没有得到它的主要好处,却不得不承受它固有的缺点,所以我们将它们排除在外。 \r \r 对于`Go`/`TinyGo` 来说,是我们熟悉的语言。它语法简单,有一定的模块化和包管理特性,有良好的异步支持,并且不需要虚拟机。是编写插件的更好选择。考虑到“TinyGo”编译后的产品文件大小具有显着优势,且缺失的功能主要可以通过“binding”来提供,因此优先使用“TinyGo”而不是Google官方的“Go”。 (虽然Google已经逐渐开始提供`WASI`支持,但仍处于非常早期的阶段)\r \r 关于“Rust”,它是探测组熟悉的语言。编译后的产品文件小、高性能、免GC。适合于变化相对不频繁的部分的实现,例如公共库、探针功能辅助插件等。由于学习/编写/调试/阅读的效率相对较低,插件的主要部分不适合用Rust 编写。考虑到Rust 非常适合编写简单的逻辑,所以使用Rust 来编写一些辅助功能插件也是可以的。 \r \r 关于`Zig`,考虑到它还没有达到1.0版本,不适合在生产环境中使用。 \r \r *由于**WASM** 的模型限制,异步功能略有限制,并且可能会产生额外的开销。例如,**TinyGo** **文档**有以下关于并行性的描述:*\r \r 对其他平台(例如WebAssembly)的支持有点受限: 调用阻塞函数可能会分配堆内存。\r \r 虽然这些语言可以说对‘WebAssembly’有很好的支持,但实际的选择还需要考虑很多其他因素,比如‘运行时’的支持、‘WASI’的支持、‘绑定’的书写便利性以及我们的第二个问题。 \r \r ####解决通信问题的现有方案\r \r \r 关于第二个问题,有四种选择,分别是手动使用运行时提供的API维护、使用[waPC](https://wapc.io/)进行跨模块进程调用、使用[Extism](https://extism.org/)封装插件调用逻辑、使用[wit-bindgen](https://github.com/bytecodealliance/wit-bindgen)提供`WIT`格式支持。其中,第一类手动维护支持最为稳定,第二类和第三类是基于第一类提供的不同表示形式的封装,第四类WIT是WASI的preview2的一部分,即将成为正式标准。 \r \r [WIT](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md)即`Wasm Interface Type`,是字节码联盟经过第一阶段实验后确定的第二阶段接口标准格式。它为“WebAssembly”组件模型的导入和导出提供了统一的高阶数据类型表示标准。如果不出意外,第二阶段确定的内容在第三阶段不会有任何改变(根据官方的说法,第三阶段只是提供最终解决问题的机会),而是在修复一些问题后直接成为最终的官方标准。 \r \r 一些厂商已经开始在其商业产品中使用`WIT`支持,例如著名的Serverless厂商Fermyon的产品[Spin](https://github.com/fermyon/spin)。 \r \r 除了常见的整数、浮点数、字符串之外,WIT 还提供了包括tuple、option、result、interface、record、variant、enum、union、flags 等高级类型,非常吸引人。与“wit-bindgen”和“Rust”一起食用很舒服。 \r \r 虽然前三个的开发时间较长(因此可能更稳定),但我们打算遵循未来的标准,参与标准的生态共建。 \r \r ## 最终选择及原因\r 在比较了多种编程语言在性能、安全性、易用性等方面的优缺点后,我们最终选择了`TinyGo`作为主要的插件开发语言,并辅以`Rust`。 \r \r `TinyGo` 在编译后的产品文件大小、性能和易用性方面都有明显的优势,适合作为插件的主要部分使用。 Rust 在高性能和安全性方面表现良好,适合实现变化相对不频繁的部分,例如公共库、探针功能辅助插件等。 \r \r 另外,我们对`zig`和[`nim`](https://nim-lang.org/)也很感兴趣,会持续关注它们是否能够满足生产需求。 \r \r 通过为牧云插件系统选择合适的开发语言,可以更好地满足主机安全系统的需求,提高插件性能和安全性,降低开发和维护成本。未来,我们将持续关注‘WASM’生态的发展,优化和完善插件系统,以满足不断变化的安全需求。 \r \r ## 推荐相关博文\r \r 上一篇:**【牧云插件系统技术选型中探针开发用什么? 】](https://stack.chaitin.com/techblog/detail?id=83)**\r \r 系列文章目录:【【预览】主机Agent插件引擎开发故事总结】(https://stack.chaitin.com/techblog/detail?id=82)\r \r 1. **[[揭秘牧云插件开发者的创新之路:从无法解决的问题到“充满乐趣”]](https://stack.chaitin.com/techblog/detail?id=72)**\r 2. **[【牧云插件系统面向未来的设计原则]](https://stack.chaitin.com/techblog/detail?id=73)**\r 3. **[【牧云插件系统选择之争]](https://stack.chaitin.com/techblog/detail?id=77)**\r 4. **【【牧云插件系统技术选型中探针开发用什么? 】](https://stack.chaitin.com/techblog/detail?id=83)**\r 5. **【【牧云插件系统技术选型及插件开发? 】](https://stack.chaitin.com/techblog/detail?id=85)**\r 6. **[【牧云插件系统技术选型插件通信框架大PK]](https://stack.chaitin.com/techblog/detail?id=94)**\r 7. **[【牧云插件系统技术选型:大家都会抱怨的序列化方法选择之争]](https://stack.chaitin.com/techblog/detail?id=97)**\r 8. **[【牧云插件系统技术选型:应该选择哪种WASM运行时?如果不选择这个,就会出大问题! 】](https://stack.chaitin.com/techblog/detail?id=105)**
创建帐户或登录后发表意见