沙箱定义
沙箱技术本质上是将不信任的程序隔离在沙箱内进行运行操作,防止影响其它应用和服务。
基于隔离的沙箱
该类型的沙箱将应用的执行环境从操作系统中隔离出来形成一个独立的执行环境。
基于规则的沙箱
该类型沙箱是使用规则的方式控制每个应用的权限,且沙箱之间可以分享操作系统的逻辑资源。
VM安全隔离架构
虚拟机的安全隔离能力可以看水平和垂直维度
水平维度:虚拟机hypervisor层水平分隔不同的guest os,保证guest os 的漏洞不会影响其它的guest os ;
垂直维度:hyperviso层分隔不同的操作系统(guest os 和host os的内核不共享)和物理硬件,控制guest os中漏洞不会影响到宿主机上的操作系统和物理硬件。
Container
容器的安全隔离能力借鉴了 VM 的这种分隔的思想,将不同的应用水平分隔在不同容器环境中运行,如果容器中的应用存在漏洞,它是不会直接影响到其他应用。但是在垂直分隔的维度上,容器和容器之间共享同一个内核,没有带来完全意义上的隔离,因此如果一个容器触发了一个内核漏洞导致内核崩溃,就会导致整个系统上的其他容器中的应用也会受到影响。
WASM+WASI
从语言设计上就利用 SFI 软件故障隔离的概念,将 WASM 应用程序的故障都隔离约束在自己单独的线性地址空间中,确保应用程序不会跳转到故障域之外的地址空间,实现安全隔离的目的。并且 WASM 的虚拟指令集都是纯计算指令集,不会像 VM 或 Container 需要涉及到对内核或者硬件资源的访问,因此通过 SFI 机制就可以保障程序的漏洞不会逃逸和扩散。
WASI概念则是基于前面第二种基于规则的沙箱技术的安全模型,用规则的方式控制每个 WASM 应用的权限,需要什么能力,就需要显示地为应用配置对应的规则。
WASM+WASI 的沙箱技术汲取了基于隔离沙箱技术和基于规则沙箱技术的优点,提供的轻量级沙箱技术。
WASM接入云原生生态
WASM 沙箱在保证安全隔离的前提下,可以获得毫秒级冷启动速度,如果接入到云原生生态中,容器运行时首先要考虑的问题是如何发挥 WASM 极速启动的优势。
Kuasar 基于 Sandbox API 接口的设计将 sandbox 的概念具体的从 containerd-shim 进程模型中解放出来,可以直接映射到实际的沙箱对象上,例如轻量级虚拟机类型的MicroVM沙箱,WASM 语言虚拟机类型的沙箱。
Kuasar 对接 Wasmtime
Kuasar 对接 Wasmtime 运行时的架构采用的是协程模型,wasm-sandboxer 沙箱管理模块和 wasm-task 容器管理模块均运行在同一个进程中,由 wasm-task 创建的 WASM沙箱实例也同样运行在 wasm-sandboxer 进程中,这样设计的好处是可以充分利用单进程内的协程调度机制来降低 WASM 沙箱实例的调度开销,避免传统每个WASM实例单进程模式对系统PID资源的消耗,提高单节点上 WASM 沙箱实例的部署密度。
WASM 沙箱实例中 WASM 函数的 IO 流则是通过命名管道与容器引擎进行对接,让容器引擎可以收集到 WASM 函数的输出内容。
Kuasar 对接 WasmEdge
Kuasar 对接 WasmEdge 运行时的架构与 Wasmtime 运行时稍有一些不同,当 wasm-task 接收到创建 WASM 沙箱的请求时,会通过 fork 系统调用创建出一个新的进程来实例化 WASM 沙箱,这样设计的原因是 WasmEdge 运行时当前暂不支持在一个进程中同时重定向不同 WASM 沙箱实例的标准输入和标准输出。
实例
1. 下面以 Wasmtime 作为 WASM 运行时为例进行介绍说明,WasmEdge 运行时的编译与运行步骤类似。
2. Kuasar 支持 WASM 沙箱是一个预览特性,不支持应用在商用环境中。
环境准备
1. 准备一个 openEuler 23.09 版本的 x86 环境
2. 安装 Rust 语言编译工具链,安装方法参考 Rust 官方安装教程[5]
3. 安装 iSulad 容器引擎,安装方法参考 iSulad 容器引擎 - 安装与配置[6]
4. 安装 isula-build 容器镜像构建工具,安装方法参考isula-build 容器镜像构建 - 安装[7]
制作 WASM 应用容器镜像
1.编译 WASM 格式目标应用程序
# 以 runwasi 项目中 wasi-demo-app 应用为例介绍
$ git clone https://github.com/containerd/runwasi.git
$ cd runwasi/crates/wasi-demo-app
# 安装 rust wasm32-wasi 编译工具链
$ rustup target add wasm32-wasi
# 将 "wasi-demo-app" 程序编译成 wasm 目标格式
$ cargo build --target=wasm32-wasi --release
# 编译生成的 wasm 模块会存放在 target/wasm32-wasi/release/ 目录下
$ ls runwasi/target/wasm32-wasi/release/*.wasm
target/wasm32-wasi/release/wasi-demo-app.wasm
2.创建一个空的目录,将上一步生成的 wasm 格式应用文件拷贝到该空目录中
$ mkdir build && cd build
$ cp target/wasm32-wasi/release/wasi-demo-app.wasm .
3. 编写容器镜像构建所需的 Dockerfile
$ cat Dockerfile
FROM scratch
ADD wasi-demo-app.wasm /
4. 通过 isula-build 构建 WASM 应用容器镜像,并直接导入到 iSulad 容器引擎
$ isula-build ctr-img build -f Dockerfile -o isulad:wasi-demo-app:v1
STEP 1: FROM scratch
STEP 2: ADD wasi-demo-app.wasm /
Getting image source signatures
Copying blob sha256:2ed8f32f0a3bd46fddc96fbe1639eaebd8a3d0e49c9c284ddf24619300fcfab5
Copying config sha256:59b1ccb0ae18329439e0b3995de75df9a021b5096afaad7a72925fa2b85f4ae4
Writing manifest to image destination
Storing signatures
Committed stage 0 with ID: 59b1ccb0ae18329439e0b3995de75df9a021b5096afaad7a72925fa2b85f4ae4
Getting image source signatures
Copying blob sha256:2ed8f32f0a3bd46fddc96fbe1639eaebd8a3d0e49c9c284ddf24619300fcfab5
Copying config sha256:59b1ccb0ae18329439e0b3995de75df9a021b5096afaad7a72925fa2b85f4ae4
Writing manifest to image destination
Storing signatures
Build success with image id: 59b1ccb0ae18329439e0b3995de75df9a021b5096afaad7a72925fa2b85f4ae4
$ isula images
REPOSITORY TAG IMAGE ID CREATED SIZE
wasi-demo-app latest 59b1ccb0ae18 2023-11-21 20:30:53 108.229KB
编译运行 WASM 沙箱运行时
1. 编译基于Wasmtime运行时的wasm-sandboxer组件
# 下载 kuasar 项目源码
$ git clone https://github.com/kuasar-io/kuasar.git
# 切换到 wasm沙箱模块源码目录下
$ cd kuasar/wasm
# 编译 wasm-sandboxer 组件
$ cargo build --release --features=wasmtime
# 生成的 wasm-sandboxer 二进制可执行文件路径
$ ls target/release/wasm-sandboxer
2. 启动 wasm-sandboxer 沙箱运行时进程
RUST_LOG=info wasm-sandboxer --listen /run/wasm-sandboxer.sock --dir /run/kuasar-wasm
3. 修改 iSulad 容器引擎配置支持 WASM 沙箱运行时
// 在 iSulad 容器引擎的配置文件 /etc/isulad/daemon.json 中添加下面有关 wasm 沙箱运行时的配置信息
{
......
"cri-runtimes": {
"wasm": "io.containerd.wasm.v1"
},
"cri-sandboxers": {
"wasm": {
"name": "wasm",
"address": "/run/wasm-sandboxer.sock"
}
},
......
}
4. 重启 iSulad 使配置生效
systemctl restart isulad
创建 WASM 沙箱并执行 WASM 函数
下面的脚本中定义了 WASM 类型 Pod 沙箱和运行 WASM 函数容器的配置信息,通过 crictl 命令来创建 WASM 沙箱并执行 WASM 函数:
$ cat run_example_wasm_container_with_isulad.sh
set -e
# Prepare for the wasm Pod and Container config file.
touch pod.json container.json
current_timestamp=$(date +%s)
cat > pod.json <<EOF
{
"metadata": {
"name": "test-sandbox$current_timestamp",
"namespace": "default"
},
"log_directory": "/tmp",
"linux": {
"security_context": {
"namespace_options": {
"network": 2,
"pid": 1
}
}
}
}
EOF
cat > container.json <<EOF
{
"metadata": {
"name": "wasm_$current_timestamp",
"namespace": "default"
},
"image": {
"image": "wasi-demo-app:latest"
},
"command": [
"/wasi-demo-app.wasm","daemon"
],
"log_path":"wasm.log",
"linux": {
"security_context": {
"namespace_options": {
"network": 2,
"pid": 1
}
}
}
}
EOF
# Run a wasm container
crictl run --runtime="wasm" --no-pull container.json pod.json
rm -f container.json pod.json
执行上述脚本程序后,可以通过crictl命令观测到 WASM 沙箱和 WASM 函数的运行的状态。
$ crictl ps -a
CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID POD
0b1dee6dc1a6c wasi-demo-app:latest 2 seconds ago Running wasm 0 37fe4fc2fc358 unknown
$ crictl pods
POD ID CREATED STATE NAME NAMESPACE ATTEMPT RUNTIME
37fe4fc2fc358 27 seconds ago Ready test-sandbox1700546390 default