2. 云原生应用攻击面建模

云原生应用不是“一个跑在云上的程序”,而是一条由源码、依赖、构建产物、运行时工作负载、编排控制面、服务网格数据面以及底层云资源共同组成的交付与运行链。攻击面建模的目标,是把这条链上所有可能被外部或内部攻击者触达、影响、利用的入口、资产与信任关系显式地列出来,并标注它们之间的可达路径。只有先看清“有哪些资产、谁信任谁、谁能到谁”,后续的漏洞优先级排序、策略收敛和运行时检测才有依据。

一、攻击面建模的六个层次

工程上通常把云原生攻击面拆成六个层次,每一层的资产类型、暴露方式和典型风险都不同:

  • 代码与依赖层:源码仓库、CI 流水线、第三方包(npm、PyPI、Maven、Go module)、构建脚本。风险集中在供应链投毒、硬编码凭据、依赖混淆与构建环境被篡改。
  • 镜像与制品层:容器镜像、Helm Chart、SBOM、镜像仓库。风险集中在基础镜像过期、镜像层中残留密钥、镜像签名缺失、仓库可被匿名拉取或推送。
  • 容器与运行时层:容器进程、挂载卷、命名空间、capabilities、seccomp/AppArmor 配置。风险集中在特权容器、宿主机目录挂载、容器逃逸。
  • Kubernetes 控制面与工作负载层:API Server、etcd、kubelet、ServiceAccount、RBAC、Ingress、NetworkPolicy、Secret/ConfigMap。风险集中在过度授权、匿名访问、Secret 明文、缺少网络隔离。
  • 服务网格层:Sidecar、mTLS 证书、VirtualService、AuthorizationPolicy、网关。风险集中在 mTLS 未强制、授权策略过宽、控制面证书泄露。
  • 云基础设施层:VPC、安全组、IAM Role、对象存储、KMS、负载均衡、托管 Kubernetes 控制面。风险集中在公网暴露、IAM 权限过大、跨账号信任链被滥用。

这六层不是并列的,而是存在明确的“上游影响下游”关系:代码层的依赖漏洞会进入镜像,镜像配置会决定容器权限,容器权限会影响能否触达 Kubernetes API,Kubernetes 中的 ServiceAccount 又可能映射到云 IAM Role。因此攻击面建模必须沿着这条链做可达性分析,而不是孤立地扫描每一层。

云原生应用六层攻击面与信任传递链 ① 代码与依赖层 源码仓库 · CI 流水线 · 第三方包 · 构建脚本 · 硬编码凭据 供应链投毒 ② 镜像与制品层 容器镜像 · Helm Chart · SBOM · 镜像仓库 · 签名与标签 镜像残留密钥 ③ 容器与运行时层 容器进程 · 挂载卷 · capabilities · seccomp/AppArmor · 命名空间 特权容器逃逸 ④ Kubernetes 控制面与工作负载层 API Server · etcd · kubelet · RBAC · ServiceAccount · Ingress · Secret 过度授权 ⑤ 服务网格层 Sidecar · mTLS 证书 · VirtualService · AuthorizationPolicy · 网关 mTLS 未强制 ⑥ 云基础设施层 VPC · 安全组 · IAM Role · 对象存储 · KMS · 负载均衡 · 托管控制面 IAM 权限过大 信任传递方向 攻击者可达路径

二、资产建模:从“清单”到“关系图”

很多团队的攻击面梳理停留在“资产清单”阶段:列出有多少个镜像、多少个 Namespace、多少个 IAM Role。但清单无法回答“从公网 Ingress 到数据库 Secret 需要经过几跳”。资产建模要做的是把清单升级为带属性的关系图。每个节点至少应包含以下字段:

字段含义示例
asset_id全局唯一标识k8s://prod/ns-payment/deploy-checkout
asset_type资产类型Deployment / Image / IAMRole / ServiceAccount
exposure暴露面等级internet / intranet / cluster-internal / none
privilege权限等级cluster-admin / namespace-admin / read-only
data_class数据敏感度PII / PCI / internal / public
trust_edges信任关系边SA→IAMRole、Ingress→Service、Sidecar→CA

其中 trust_edges 是攻击面建模的核心。以 Kubernetes 为例,一条典型的信任边是:ServiceAccount → IAM Role(通过 IRSA 或 Workload Identity)。如果某个 ServiceAccount 被绑定到 s3:GetObject 且资源为 *,那么任何能创建使用该 SA 的 Pod 的攻击者,就等价于获得了跨账号读取对象存储的能力。这条边在清单里看不见,但在关系图里必须显式画出。

三、可达性分析:把“能到”变成“能利用”

有了关系图之后,攻击面建模的第二步是计算从“入口节点”到“高价值目标节点”的可达路径。入口节点包括:公网 Ingress、LoadBalancer Service、NodePort、被允许匿名访问的 API Server 端点、可被外部推送的镜像仓库。高价值目标包括:持有云 IAM 凭据的 ServiceAccount、etcd 数据、KMS 密钥、含 PII 的数据库 Secret。

可达性分析可以用图遍历实现。下面是一段简化的 Python 示例,用 NetworkX 在资产关系图上查找从公网入口到敏感资产的最短路径:

import networkx as nx

G = nx.DiGraph()
# 节点:资产;边:信任/可达关系
G.add_edge("internet", "ingress-nginx", exposure="internet")
G.add_edge("ingress-nginx", "svc-checkout", exposure="cluster-internal")
G.add_edge("svc-checkout", "sa-checkout", privilege="namespace-admin")
G.add_edge("sa-checkout", "iam-role-payment", privilege="s3:GetObject *")
G.add_edge("iam-role-payment", "s3-pii-bucket", data_class="PII")

# 查找从公网到 PII 存储的可达路径
paths = list(nx.all_simple_paths(G, "internet", "s3-pii-bucket"))
for p in paths:
    print(" -> ".join(p))
# 输出:internet -> ingress-nginx -> svc-checkout -> sa-checkout -> iam-role-payment -> s3-pii-bucket

这条路径一旦被识别出来,就可以针对性地做收敛:给 sa-checkout 去掉 s3:GetObject *,改为只允许访问特定前缀;或者在 svc-checkout 与 iam-role-payment 之间增加 NetworkPolicy 与 IAM Condition 限制。攻击面建模的价值不在于“画出一张图”,而在于让每一条高风险路径都有明确的收敛动作和验证方式。

四、建模的落地节奏与验证

实际工程中,攻击面建模不需要一次性覆盖全部六层。建议按“先入口、后纵深”的顺序推进:第一轮先梳理公网暴露面(Ingress、LoadBalancer、NodePort、公网 API Server),第二轮补全 Kubernetes RBAC 与 ServiceAccount 到云 IAM 的信任边,第三轮再覆盖镜像仓库、CI 流水线与服务网格证书。每一轮结束后,用一条可复现的验证命令确认收敛效果,例如:

# 检查是否仍有 ServiceAccount 绑定到 cluster-admin
kubectl get clusterrolebindings -o json | \
  jq '.items[] | select(.roleRef.name=="cluster-admin") | .subjects[]'

# 检查是否有 LoadBalancer Service 直接暴露到公网
kubectl get svc -A -o json | \
  jq '.items[] | select(.spec.type=="LoadBalancer") | {ns:.metadata.namespace, name:.metadata.name}'
攻击面建模的产出物建议至少包含三样:一张带属性的资产关系图、一份按可达性排序的高风险路径清单、以及每条路径对应的收敛动作与验证命令。三者缺一不可,否则模型无法驱动实际的安全改进。

需要强调的是,攻击面是动态的:一次新的 Deployment、一次 RBAC 变更、一次 IAM Policy 调整,都可能新增一条从公网到敏感数据的路径。因此建模不是一次性项目,而应嵌入 CI/CD 与集群准入控制中,在变更发生时自动重算可达性并告警。只有这样,云原生应用保护才从“事后扫描”转向“持续收敛”。