场景
运行不可信代码
本页在一个不能访问网络、资源受限、没有 Volume 的 Workspace 里,限时运行你不信任的代码,再读回有大小上限的结果。完整程序是 examples/go/untrusted-code。
前提条件
- Go 1.27 或更新版本,以及 Kibotos 仓库的一份检出。
- ctrl 地址,以及用
kibo enrollment create --operator为这个程序生成的注册密钥(enrollment key),见身份与注册。
步骤
1. 创建受限的 Workspace
创建 Workspace 时设置限制。设置到期时间后,即使程序再也不回来,ctrl 也会删除它:
request := &ctrlv1.CreateWorkspaceRequest{
Metadata: &ctrlv1.CommandMetadata{CommandId: commandID},
Spec: &ctrlv1.WorkspaceCreateSpec{
Template: &ctrlv1.TemplateSelector{TemplateId: templateID},
Resources: &ctrlv1.ResourceRequest{CpuMillis: 500, MemoryBytes: 256 << 20, EphemeralDiskBytes: 1 << 30},
Volume: &ctrlv1.WorkspaceVolumeBinding{Mode: ctrlv1.WorkspaceVolumeMode_WORKSPACE_VOLUME_MODE_NONE},
NetworkPolicy: &ctrlv1.NetworkPolicy{Mode: ctrlv1.NetworkPolicyMode_NETWORK_POLICY_MODE_DENY_ALL},
},
Expiration: &ctrlv1.WorkspaceExpirationPolicy{
Timeout: durationpb.New(10 * time.Minute),
Action: ctrlv1.WorkspaceExpirationAction_WORKSPACE_EXPIRATION_ACTION_DELETE,
},
}2. 限时运行代码
上传代码,用 TimeoutMillis 启动。超过时限后进程会被终止:
stream, err := session.StartProcess(ctx, &ctrlv1.StartRequest{
Argv: []string{"/tmp/job.sh"},
TimeoutMillis: 30_000,
Mode: &ctrlv1.StartRequest_Pipe{Pipe: &ctrlv1.ProcessPipeMode{}},
})3. 检查进程怎样结束
退出事件说明代码是运行完了还是超时了。被终止的进程没有有意义的退出码,所以先看原因:
switch exited.GetTerminationReason() {
case ctrlv1.ProcessTerminationReason_PROCESS_TERMINATION_REASON_EXITED:
if code := exited.GetExitCode(); code != 0 {
return fmt.Errorf("the code exited with %d", code)
}
case ctrlv1.ProcessTerminationReason_PROCESS_TERMINATION_REASON_TIMED_OUT:
return errors.New("the code ran past its time limit and was killed")
default:
return fmt.Errorf("the code ended with %s, signal %d", exited.GetTerminationReason(), exited.GetSignal())
}4. 读取有上限的结果
输出与结果文件同样不可信。每样只保留有限的量,用完后删除 Workspace:
result, err := io.ReadAll(io.LimitReader(file, maxOutput))5. 运行程序
内置脚本会尝试访问网络并写一个结果文件。传 -code 运行自己的脚本:
go run ./examples/go/untrusted-code -endpoint https://ctrl.example.com:18092 \
-identity "$HOME/.local/state/kibotos-untrusted-code" -enrollment-key-file untrusted-code.keycreated Workspace ws-06gvz1gwahz9f738p47k0d7zpg
output:
wget: bad address 'example.com'
network: unreachable
result: computed 42
deleted Workspace ws-06gvz1gwahz9f738p47k0d7zpg用 E2B SDK
SDK 默认请求公网访问,所以要明确关闭。命令超过时限时会抛出错误:
import { Sandbox } from 'e2b';
const sandbox = await Sandbox.create('kibotos-minimal', {
allowInternetAccess: false,
timeoutMs: 10 * 60_000,
lifecycle: { onTimeout: 'kill', autoResume: false },
});
try {
await sandbox.files.write('/tmp/job.sh', 'echo computed 42 > /tmp/result.txt\n');
await sandbox.commands.run('sh /tmp/job.sh', { timeoutMs: 30_000 });
console.log(await sandbox.files.read('/tmp/result.txt'));
} finally {
await sandbox.kill();
}每个 sandbox 的资源由网关设置,SDK 不能单独调低,见资源。
注意
- 隔离来自 MicroVM。代码可以在里面以 root 运行,但影响不到宿主机和其他 Workspace,见隔离。
- 为每个用户或任务使用各自的 Workspace。一个 Workspace 是一个信任边界,停止它不会清掉文件。
- 不能访问网络的 Workspace 仍然响应你的程序发来的数据面调用。