当多个用户的 Agent 共用一个集群执行代码或调用工具时,应将不同信任来源的任务放入不同沙箱。本文模拟用户 A 和用户 B 的两个任务,在同一节点创建两个 Cube Pod,验证它们的运行环境、进程和工作文件是否隔离,以及一个任务异常退出时另一个任务能否继续运行。
Cube 为每个 Pod 创建独立虚拟机和 Guest 内核。同一 Pod 内的业务容器、初始化容器和 sidecar 共用一个沙箱;如果需要隔离两个用户,应创建两个 Pod,不能只创建同一 Pod 中的两个容器。
本文验证什么
检查项 | 操作 | 结果如何判断 |
运行环境 | 核对 RuntimeClass、实际运行时记录和两个 Pod 的内核启动标识 | 确认实际使用 Cube。 |
进程可见性 | 分别查看两个 Pod 内的进程 | 能看到自己的任务进程,看不到对方的任务进程。 |
文件隔离 | 在相同目录分别写入不同文件 | 两个 Pod 各自保留自己的内容,不能通过本地目录看到对方文件。 |
异常退出影响 | 让 A 的测试任务以非零退出码结束 | A 失败,B 的同一个容器继续处理文件,未发生重启。 |
这些检查用于验收配置及有限场景下的隔离行为,不是漏洞安全测试。Cube 增加的边界是每 Pod 独立虚拟机及 Guest 内核。
开始前准备
1. 在 Agent Runtime 控制台 准备专用测试集群和节点池,确认一个支持 Cube 的节点已就绪。创建方式请参见 创建集群和节点池。
2. 为本次测试创建专用命名空间,例如
cube-isolation-check。下文使用这个名称;若已存在同名资源,换用新的测试命名空间并同步修改清单和命令。3. 通过控制台获取访问配置,连接集群。执行命令的账号需要在测试命名空间读取 Pod、事件、日志和执行容器命令的权限。
4. 准备节点可拉取的 BusyBox 1.36 工具镜像,包含
sh、ps、cat、tr、sleep 和 touch。建议使用已审核镜像的固定 digest;私有镜像同时配置拉取凭证。5. 确认目标节点能同时容纳两个测试 Pod。每个容器请求 100m CPU、128Mi 内存,上限为 500m CPU、256Mi 内存;还需计入目标 RuntimeClass 的额外资源开销。本测试不创建持久卷或负载均衡器。
下文通过控制台创建和删除资源,用 kubectl 采集结果。节点有污点时,仅添加管理员确认过的必要容忍配置,不移除节点已有污点。
在同一终端配置连接并查询环境:
export KUBECONFIG='/absolute/path/to/agc-kubeconfig'export AGC_NAMESPACE='cube-isolation-check'kubectl config current-contextkubectl version -o yamlkubectl get runtimeclass -o yamlkubectl get nodes -L kubernetes.io/hostname
保存测试日期、集群 Kubernetes 版本、节点内核版本、Cube 版本、RuntimeClass 名称与配置、镜像 digest。
第一步:通过控制台创建两个测试 Pod
在目标集群进入工作负载的 YAML 创建入口,选择测试命名空间。控制台跳转到关联 TKE 集群时,先核对集群 ID。将下面两份清单分别提交,并替换以下内容:
runtimeClassName: cube:使用 Cube 运行时,保留此值。REPLACE_WITH_NODE_HOSTNAME:同一个目标节点的 kubernetes.io/hostname 标签值,不一定等于节点名称。REPLACE_WITH_BUSYBOX_IMAGE:准备好的完整镜像地址及 digest。两个 Pod 必须使用同一个节点标签值。这样验证的是共享物理节点时的隔离,避免把不同节点带来的分离当成 Cube 的效果。
用户 A 的任务
apiVersion: v1kind: Podmetadata:name: cube-isolation-anamespace: cube-isolation-checklabels:app: cube-isolation-checkspec:runtimeClassName: cubenodeSelector:kubernetes.io/hostname: REPLACE_WITH_NODE_HOSTNAMErestartPolicy: NeverautomountServiceAccountToken: falsesecurityContext:runAsNonRoot: truerunAsUser: 1000runAsGroup: 1000fsGroup: 1000seccompProfile:type: RuntimeDefaultcontainers:- name: taskimage: REPLACE_WITH_BUSYBOX_IMAGEcommand: ["/bin/sh", "-c"]args:- |echo 'agent-a ready'while [ ! -f /work/stop ]; do sleep 1; doneecho 'test task exits with code 42'exit 42- agc-isolation-aresources:requests:cpu: 100mmemory: 128Milimits:cpu: 500mmemory: 256MisecurityContext:allowPrivilegeEscalation: falsereadOnlyRootFilesystem: truecapabilities:drop: ["ALL"]volumeMounts:- name: workmountPath: /workvolumes:- name: workemptyDir:sizeLimit: 16Mi
用户 B 的任务
apiVersion: v1kind: Podmetadata:name: cube-isolation-bnamespace: cube-isolation-checklabels:app: cube-isolation-checkspec:runtimeClassName: cubenodeSelector:kubernetes.io/hostname: REPLACE_WITH_NODE_HOSTNAMErestartPolicy: NeverautomountServiceAccountToken: falsesecurityContext:runAsNonRoot: truerunAsUser: 1000runAsGroup: 1000fsGroup: 1000seccompProfile:type: RuntimeDefaultcontainers:- name: taskimage: REPLACE_WITH_BUSYBOX_IMAGEcommand: ["/bin/sh", "-c"]args:- |echo 'agent-b ready'while [ ! -f /work/stop ]; do sleep 1; doneecho 'test task exits with code 42'exit 42- agc-isolation-bresources:requests:cpu: 100mmemory: 128Milimits:cpu: 500mmemory: 256MisecurityContext:allowPrivilegeEscalation: falsereadOnlyRootFilesystem: truecapabilities:drop: ["ALL"]volumeMounts:- name: workmountPath: /workvolumes:- name: workemptyDir:sizeLimit: 16Mi
两个
/work 分别来自各自的 emptyDir,没有共享 PVC、hostPath、Secret 或服务账号令牌。这里模拟任务临时目录;如果业务主动挂载同一个工作区或配置同一套数据凭证,相应的数据共享不能由沙箱自动消除。确认两个 Pod 已运行:
kubectl wait -n "$AGC_NAMESPACE" --for=condition=Ready pod/cube-isolation-a pod/cube-isolation-b --timeout=180skubectl get pods -n "$AGC_NAMESPACE" -l app=cube-isolation-check -o widekubectl get pods -n "$AGC_NAMESPACE" -l app=cube-isolation-check -o yaml
核对两者的
spec.nodeName 相同、spec.runtimeClassName 为目标 Cube 运行时,容器状态正常。第二步:核对独立运行环境
分别读取内核版本和内核启动标识:
kubectl exec -n "$AGC_NAMESPACE" cube-isolation-a -c task -- sh -c 'uname -r; cat /proc/sys/kernel/random/boot_id'kubectl exec -n "$AGC_NAMESPACE" cube-isolation-b -c task -- sh -c 'uname -r; cat /proc/sys/kernel/random/boot_id'
boot_id 是内核启动标识。记录两个值并比较:两个沙箱可使用同一版本的 Guest 内核,因此 uname -r 相同是正常现象;不同的 boot_id 可以作为不同内核运行实例的辅助证据。第三步:检查进程可见性
两个任务的主进程分别带有
agc-isolation-a 和 agc-isolation-b 标识。遍历当前可见进程,读取完整命令行,避免进程列表截断标识造成误判:AGC_PROCESS_SCAN='failed=0for d in /proc/[0-9]*; do[ -d "$d" ] || continueprintf "PID %s: " "${d##*/}"if ! tr "\\000" " " < "$d/cmdline"; thenecho "READ_FAILED"failed=1fiechodoneexit "$failed"'kubectl exec -n "$AGC_NAMESPACE" cube-isolation-a -c task -- sh -c "$AGC_PROCESS_SCAN"kubectl exec -n "$AGC_NAMESPACE" cube-isolation-b -c task -- sh -c "$AGC_PROCESS_SCAN"
预期 A 的完整命令行输出包含自己的
agc-isolation-a,不包含 B 的 agc-isolation-b;B 反之。还可能看到 sleep 和本次检查进程。分别检查两条命令的退出码;出现 READ_FAILED、权限错误或进程在读取期间退出时,保留输出并重新采集。两个 Pod 都出现 PID 1 并不冲突:PID 数字属于各自可见的进程空间。不要用另一个 Pod 的 PID 数字在当前 Pod 执行 kill,这可能终止当前 Pod 中恰好同号的进程,无法证明跨沙箱访问。
此项证明本次任务的进程可见性分离。
第四步:检查工作文件隔离
分别向相同路径写入不同内容,再创建各自独有的文件:
kubectl exec -n "$AGC_NAMESPACE" cube-isolation-a -c task -- sh -c 'echo agent-a > /work/owner.txt; echo only-a > /work/a-only.txt'kubectl exec -n "$AGC_NAMESPACE" cube-isolation-b -c task -- sh -c 'echo agent-b > /work/owner.txt; echo only-b > /work/b-only.txt'kubectl exec -n "$AGC_NAMESPACE" cube-isolation-a -c task -- sh -c 'cat /work/owner.txt; cat /work/a-only.txt; test ! -e /work/b-only.txt && echo peer-file-absent'kubectl exec -n "$AGC_NAMESPACE" cube-isolation-b -c task -- sh -c 'cat /work/owner.txt; cat /work/b-only.txt; test ! -e /work/a-only.txt && echo peer-file-absent'
A 应输出
agent-a、only-a、peer-file-absent;B 应输出 agent-b、only-b、peer-file-absent。命令退出码也应为 0。此项验证两个独立临时目录未串用。
第五步:验证单个任务异常退出的影响
先记录 B 的 Pod UID、容器 ID、启动时间、重启次数和内核启动标识:
kubectl get pod -n "$AGC_NAMESPACE" cube-isolation-b -o yamlkubectl exec -n "$AGC_NAMESPACE" cube-isolation-b -c task -- cat /proc/sys/kernel/random/boot_id
让 A 的测试进程检测到停止文件后以退出码 42 结束:
kubectl exec -n "$AGC_NAMESPACE" cube-isolation-a -c task -- touch /work/stopkubectl wait -n "$AGC_NAMESPACE" --for=jsonpath='{.status.phase}'=Failed pod/cube-isolation-a --timeout=60skubectl get pod -n "$AGC_NAMESPACE" cube-isolation-a -o jsonpath='{.status.containerStatuses[0].state.terminated.exitCode}{"\\n"}'kubectl logs -n "$AGC_NAMESPACE" cube-isolation-a -c task
随后验证 B 仍能连续处理文件:
kubectl exec -n "$AGC_NAMESPACE" cube-isolation-b -c task -- sh -c 'i=0; while [ "$i" -lt 6 ]; do cat /work/owner.txt || exit 1; echo "$i" >> /work/progress.txt || exit 1; i=$((i+1)); sleep 5; done; cat /work/progress.txt'kubectl get pod -n "$AGC_NAMESPACE" cube-isolation-b -o yamlkubectl exec -n "$AGC_NAMESPACE" cube-isolation-b -c task -- cat /proc/sys/kernel/random/boot_id
通过条件:A 的退出码为 42;B 在约 30 秒观察期间完成六次文件处理,仍为 Running,且 Pod UID、容器 ID、启动时间、重启次数和启动标识与操作前一致。命令超时、重启或检查缺失需要记录原因。
此项仅验证一次应用进程异常退出没有使另一任务停止,不代表 Guest 内核崩溃、内存耗尽、CPU 争抢或宿主机故障已完成验证。本文不执行这些压力或破坏性测试。
记录结果
检查项 | 实际结果 | 应保留的证据 |
两个 Pod 使用 Cube 且位于同一节点 | 按本次观察填写 | Pod YAML、RuntimeClass、节点名称、镜像 ID。 |
两个 Pod 对应独立运行实例 | 按本次观察填写 | 启动标识、Pod UID 与运行时侧 sandbox / VM 实例对应记录。 |
进程可见性 | 按本次观察填写 | 两侧完整可见进程命令行及采集退出码。 |
临时工作文件隔离 | 按本次观察填写 | 两侧文件内容、对方文件不存在及命令退出码。 |
A 异常退出后 B 继续运行 | 按本次观察填写 | A 的退出码;B 前后身份、重启次数及文件处理输出。 |
可以填写“通过、失败、未验证”,并注明判断依据。不要把预期输出当作实测结果,也不要用整套 Kubernetes E2E 的通过数量代替这些隔离检查。
沙箱隔离不等于网络互相断开。两个 Pod 仍可能按集群网络配置互访;限制跨用户访问需要网络策略或其他访问控制,并另行验证。本文也未验证 RBAC、共享工作区授权、外部服务凭证隔离或侧信道防护。
一次实测结果
在 Agent 集群的同一个原生节点上,通过 TKE 控制台 YAML 入口创建测试命名空间及 Pod。使用本文的安全配置和资源规格,两个 Pod 均以非 root 用户运行,未挂载服务账号令牌、共享持久卷或宿主机目录。
环境项 | 本次配置 |
节点系统 | TencentOS Rainbow 1;宿主内核 6.6.110-42.45.pvm.host.cpu.002.tl4.x86_64 |
容器运行时 | containerd 2.2.5-tke.2;RuntimeClass cube |
实际 VMM 版本 | 4b3b20937d41259a-trace-0,从运行中进程路径核对 |
Guest 内核 | 6.6.110-42.45.pvm.guest.cpu.002.tl4.x86_64 |
工具镜像 | BusyBox 1.36.1;digest sha256:73aaf090f3d85aa34ee199857f03fa3a95c8ede2ffd4cc2cdb5b94e566b11662 |
单容器资源 | requests:100m CPU / 128Mi;limits:500m CPU / 256Mi |
RuntimeClass overhead | 每 Pod 250m CPU / 256Mi 内存 |
检查项 | 本次结果 | 实际观察 |
同节点使用 Cube | 通过 | 两个 Pod 位于同一节点,RuntimeClass 为 cube,CRI 中实际 runtimeHandler 也为 cube。 |
独立运行实例 | 通过 | 两个 Pod UID 分别关联不同 sandbox、runtime lease、Shim 和 VMM worker;两个 Guest 的 boot_id 不同,且不同于宿主机。 |
进程可见性 | 通过 | A、B 的完整进程命令行各自包含本方任务标识,没有对方任务标识;采集退出码均为 0。 |
临时工作文件隔离 | 通过 | 相同路径 owner.txt 分别读到 agent-a 和 agent-b;两侧都未发现对方独有文件。 |
A 异常退出后 B 继续运行 | 通过 | A 以退出码 42 结束;B 在约 30 秒内完成六次文件处理,Pod UID、容器 ID、启动时间、重启次数和 boot_id 均保持不变。 |
清理测试资源
1. 保存本次结果和必要日志,导出时去除凭证及不应公开的环境信息。
2. 在控制台删除本次创建的
cube-isolation-a 和 cube-isolation-b。只删除实际创建成功的资源。3. 核对测试 Pod 均已消失。如果命名空间只用于本次测试且没有其他资源,再删除该命名空间。