推荐标题:恶意 Dataset 不是文件风险,而是一条执行链 备选标题1:如果 Dataset Loader 能执行代码,隔离该怎么做 备选标题2:只做文件扫描,为什么拦不住 Dataset 攻击 备选标题3:五层边界拆解 Dataset Processing Threat Model

别把 Dataset 当成静态文件

发布边界:本文给出的是供应链威胁模型与示意配置,不是适用于所有集群的可直接部署清单;gVisor、Kata、NetworkPolicy 与工作负载身份必须按实际平台兼容性验证。

发布边界:本文给出的是供应链威胁模型与示意配置,不是适用于所有集群的可直接部署清单;gVisor、Kata、NetworkPolicy 与工作负载身份必须按实际平台兼容性验证。

核心结论:安全设计必须假设 Dataset、Loader、模板和依赖中至少有一项会执行任意代码。控制目标不是阻止所有漏洞,而是让任意代码执行无法升级为节点、凭据和集群入侵。

资产与信任边界

需要保护的资产包括:租户数据、内部 Dataset、云与集群身份、对象存储、模型与镜像 Registry、处理结果完整性、审计日志和平台可用性。

信任边界至少有五层:上传入口;Manifest 与策略层;执行 Worker;Node/Runtime;内部服务与云控制面。只在上传入口做扫描,会漏掉运行时产生的下载、动态依赖、模板展开和第二阶段 Payload。

攻击链跨过五道边界

威胁枚举

威胁典型路径核心控制
Loader 任意代码执行remote code、dynamic import、setup hook禁用默认执行、Allowlist、静态与动态分析
模板注入配置值进入解释器/模板引擎无逻辑模板、严格 Schema、输出编码
依赖投毒动态安装 PyPI/Git 依赖锁文件、私有镜像、Digest、离线依赖
宿主逃逸或过度权限privileged、hostPath、runtime socketRestricted Policy、无特权、沙箱运行时
凭据窃取env、Secret mount、metadata endpoint短期工作负载身份、无默认 Token
横向移动内部 DNS/API/Registry 可达默认拒绝 Egress、目的地 Allowlist
结果投毒修改处理结果、缓存或索引内容寻址、签名、双通道验证
日志规避删除容器或短命进程Node/控制面外部不可变采集

先把执行计划变成 Manifest

Canonical Manifest

所有待处理资产先归一化为可签名 Manifest。名称不是信任依据,Digest 和解析后的实际执行计划才是。

apiVersion: processing.ascendlab.dev/v1
kind: DatasetJobManifest
metadata:
  tenant: tenant_123
  job_id: job_20260718_001
spec:
  dataset:
    source: hf://org/name@revision
    content_digest: sha256:...
  loader:
    mode: declarative-only
    artifact_digest: sha256:...
    remote_code: false
  dependencies:
    lock_digest: sha256:...
    network_install: false
  execution:
    image_digest: registry/app@sha256:...
    timeout_seconds: 300
    cpu: "2"
    memory: 4Gi
  network:
    policy: deny-by-default
    allow:
      - host: object-store.internal
        port: 443
  identity:
    audience: dataset-processor
    ttl_seconds: 600

Fingerprint 应覆盖规范化后的 Dataset Revision、Loader、模板、依赖锁文件、容器镜像、命令、参数、网络策略和身份策略。任何关键项变化都应重新进入审批或隔离队列。

Admission 先挡住七类越权

Admission Control

Admission 不只检查 Kubernetes 字段,还要检查业务级 Manifest:

  • 禁止 remote_code=true 进入普通 Worker Pool。
  • 镜像必须使用 Digest,不允许可变 Tag。
  • 禁止 privilegedhostNetworkhostPIDhostIPChostPath 和容器运行时 Socket。
  • 禁止自动挂载 ServiceAccount Token。
  • 只允许经过签名的 Loader、镜像和依赖清单。
  • 网络 Allowlist 必须非空且与任务类型匹配。
  • 高风险任务进入 gVisor/Kata/VM 级隔离池,而不是普通容器池。

容器加固不等于强隔离

一次性 Worker 基线

下面是示意配置,不是可直接复制到所有集群的完整生产清单:

apiVersion: batch/v1
kind: Job
metadata:
  name: dataset-job-20260718-001
  namespace: untrusted-processing
spec:
  ttlSecondsAfterFinished: 300
  template:
    spec:
      automountServiceAccountToken: false
      restartPolicy: Never
      runtimeClassName: gvisor
      securityContext:
        runAsNonRoot: true
        seccompProfile:
          type: RuntimeDefault
      containers:
        - name: worker
          image: registry/processor@sha256:...
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: ["ALL"]
          resources:
            limits:
              cpu: "2"
              memory: 4Gi
              ephemeral-storage: 8Gi
          volumeMounts:
            - name: scratch
              mountPath: /work
      volumes:
        - name: scratch
          emptyDir:
            sizeLimit: 8Gi

Restricted Pod Security、Seccomp/AppArmor/SELinux 和专用沙箱运行时解决不同层次的问题,不能互相替代。[1][4][5]

网络和身份默认都不给

默认拒绝网络

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all
  namespace: untrusted-processing
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]

随后按 FQDN Gateway、固定代理或受控对象存储端点放行。原生 NetworkPolicy 主要基于 IP/Port;域名级控制通常需要 Egress Proxy、Service Mesh 或云防火墙补充。[2]

必须显式阻断:云 Metadata Endpoint、Kubernetes API Server(除非有严格代理)、容器 Registry 管理接口、CI/CD、Secret Manager 管理面和非任务相关内部网段。

短期身份

身份应满足:任务绑定、受众限制、十分钟级 TTL、最小 Scope、不可横向复用、任务结束自动撤销。Worker 不应持有可创建新身份、读取其他租户对象或修改控制面的权限。Kubernetes Secret 需要加密、最小 RBAC 和审计,但更优方案是按任务签发短期身份,而不是挂载长期 Secret。[3]

运行时证据

每个任务至少保留:Manifest Digest、镜像 Digest、Node、Runtime Class、进程树、网络连接、文件写入摘要、身份签发与使用、策略判定、输出 Digest 和销毁时间。日志需要在 Worker 与 Node 之外写入不可变存储,防止攻击者清理现场。

用六次攻击验证控制链

验证方法

  1. 在无真实凭据的测试集群执行恶意 Loader,确认无法访问 Metadata、API Server、邻接 Pod 和宿主路径。
  2. 注入 DNS 隧道、反向连接和大流量下载,验证 Egress 阻断与告警。
  3. 尝试读取默认 ServiceAccount Token、环境变量和挂载 Secret。
  4. 用可变 Tag 替换镜像,确认 Admission 拒绝。
  5. 修改 Loader 或配置模板,确认 Fingerprint 变化触发重新审批。
  6. 删除 Pod,确认进程与网络证据仍在外部日志系统可检索。

失败模式

  • 只做病毒扫描,不做执行计划和依赖审计。
  • 允许 Worker 直接访问公网包仓库。
  • 所有任务共享同一个 Node Pool、缓存和 ServiceAccount。
  • 用 NetworkPolicy 替代沙箱运行时,或用沙箱运行时替代身份最小化。
  • 签名通过后就假设内容安全。签名只证明来源和完整性,不证明没有恶意逻辑。[6]

AscendLab 产品化方向

可构建 Dataset Processing Manifest Inspector:输入 Dataset 元数据、Loader、依赖锁文件和 Kubernetes Job,输出 Fingerprint、风险评分、Policy Violation、Network Allowlist 和可审计的 JSON/SARIF。浏览器端可完成静态 Manifest 分析;动态执行必须在隔离环境中完成。

参考资料

  1. Kubernetes Pod Security Standards
  2. Kubernetes Network Policies
  3. Kubernetes Service Accounts 与短期 Token
  4. gVisor Kubernetes Quick Start
  5. Kubernetes Linux Kernel Security Constraints
  6. Kubernetes Secrets Good Practices