Kibotos
场景

运行不可信代码

本页在一个不能访问网络、资源受限、没有 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.key
created 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 仍然响应你的程序发来的数据面调用。

下一步

本页内容