Kubescape CLI、Operator 与持续态势扫描
开发团队在合并请求里扫描 Helm Chart,报告显示高风险控制项已经消失;同一版本部署到集群后,安全平台却仍把 Pod 标成特权容器。追查才发现,CLI 扫描的是 Chart 默认 values,生产流水线传入了另一份 values,准入 Webhook 又给 live object 补了字段。三个对象都叫同一个 Deployment,却分别代表源码模板、渲染产物和 API Server 中的实际对象。问题不是 Kubescape “前后矛盾”,而是团队拿一份输入的结论替另一份输入作证。
另一处故障更隐蔽:Kubescape 的 scanner、operator 和 node-agent 都在运行,安全看板却停止更新。值班人员重启 scanner 没有改善,最终发现聚合 API 对应的 Storage 因 PVC 压力无法写入,APIService 已经不可用;同时一个 Linux 节点因 Pod Security 例外失配没有运行 node-agent。绿色 Pod 数量掩盖了两种缺证:集群对象结果写不进去,节点事实根本采不到。持续态势扫描的核心因此不是“装好一个 Chart”,而是持续证明目标是谁、谁采集、用哪套 control、结果存在哪里、哪些对象没有被评估。
先分清五个运行面,避免一个绿色分数遮住缺口
Kubescape CLI 是面向开发机和流水线的单二进制入口。它可以读取本地 YAML、JSON、Git 仓库、Helm Chart、Kustomize 目录,也可以通过当前 kubeconfig 读取集群。CLI 适合在变更进入集群前给出快速反馈,但它只认识实际交给它的输入。扫描本地清单时,它看不到准入修改后的对象、节点内核、kubelet 配置、开放端口和运行行为。
Operator 是集群内的调度与持续扫描控制面。固定 Chart 会部署 scanner、operator、漏洞扫描、Storage、node-agent、synchronizer 等组件,并创建 CRD、聚合 API、RBAC、证书和可选告警链。官方 Operator 组件说明标明旧 kollector 与 gateway 已分别从 Chart 1.24.0 和 1.25.0 起弃用并由 synchronizer 替代;升级后仍看到这些工作负载时应按遗留对象调查,不能把它们当成当前必需组件。Operator 让结果绑定 live object,并能在对象变化后重新评估;它并不会让每一种证据自动完整,节点检查仍依赖 node-agent 或 Host Sensor,历史留存仍依赖外部设计。
node-agent 是每节点的高权限采集与运行时组件。它需要主机 PID、hostPath、Linux 内核能力和 eBPF 相关访问,承担节点事实、运行时可观测、漏洞 relevancy、网络与检测能力。nodeAgent.privileged: false 只表示容器没有设置完整 privileged 标志,不代表它是普通只读 Agent。
Storage 是 Kubernetes Aggregated API Server,不是把所有大结果直接写进 etcd 的普通控制器。元数据落在 SQLite,较大的 payload 落在文件系统;Kubernetes API aggregation layer 把 kubectl 请求转到 Storage Service。provider 则是另一个边界:它可以向 CLI 或 Operator提供 framework/control/configuration,也可以接收结果;商业 ARMO Platform 是兼容 provider,不是开源 Operator 自带的本地界面。五个运行面必须分别记录版本、身份、权限、数据和故障状态。
在开发机安装 CLI,并把规则制品也锁进基线
官方CLI 安装页提供 release 二进制、安装脚本、Krew、Homebrew、PPA、OBS 与 Snap 等入口。团队分发优先选择固定 release 的二进制,保存发布地址、摘要与 SBOM;脚本入口先下载审阅再执行,不把远程 install.sh | bash 放进不可复核的引导脚本。Krew 安装后的调用形式是 kubectl kubescape。Homebrew 用户若需要 Git repository 扫描,要核对所用 formula 是否包含 Git 支持,不能只因 kubescape version 成功就假设所有输入类型都可用。
以受控的 4.x patch 为例,安装完成先做三件事:记录程序版本、核对二进制摘要、查看当前命令帮助。Linux/macOS 可用 sha256sum,Windows 使用 Get-FileHash。版本输出应与审批的 release 一致,摘要应与内部制品清单一致;帮助命令失败、摘要不符或社区包版本落后,都应在扫描前停止。
kubescape version
kubescape help
sha256sum "$(command -v kubescape)"只锁二进制仍不够。framework 与 control 是独立更新的规则制品,同一 CLI 在不同制品上可能产生不同结论。离线网络或需要可重复门禁时,先下载 artifacts,保存目录摘要,再让扫描显式读取该目录。单独使用某个 framework 时,也可以下载 framework JSON 后用 --use-from 固定输入。升级记录必须把 CLI 版本、artifact digest、control ID/定义、扫描目标身份和例外版本放在一起。
kubescape download artifacts --output ./kubescape-artifacts
find ./kubescape-artifacts -type f -print0 | sort -z | xargs -0 sha256sum > artifacts.sha256
kubescape scan --use-artifacts-from ./kubescape-artifacts ./rendered-manifests
kubescape download framework nsa --output ./nsa.json
kubescape scan framework nsa --use-from ./nsa.json ./rendered-manifests下载失败不是“无风险”,而是工具或网络失败。代理、企业 CA 和离线镜像要在流水线镜像中统一配置;缓存过期后是拒绝门禁、沿用最后一次批准制品还是切换人工审批,应由策略明确,不能静默联网取新规则。
用一个字段变化做正反实验,而不是只看总分
先准备两个 Pod。反例明确设置 privileged: true;正例禁止提权、只读根文件系统并以非 root 用户运行。镜像在团队实验中应替换成已批准的 digest,示例中的占位摘要不能直接部署。这里选择单一 control,是为了让输入字段、求值结果与资源身份之间形成一条短证据链。
# bad-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: posture-bad
namespace: posture-lab
spec:
containers:
- name: app
image: example.com/demo/app@sha256:<approved-digest>
securityContext:
privileged: true
---
# good-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: posture-good
namespace: posture-lab
spec:
securityContext:
runAsNonRoot: true
containers:
- name: app
image: example.com/demo/app@sha256:<approved-digest>
securityContext:
allowPrivilegeEscalation: false
privileged: false
readOnlyRootFilesystem: true分别执行 control 扫描,并把 JSON v2 结果作为流水线产物。预期反例能够定位到 posture-bad 的容器和失败 control,正例不再因 privileged 字段失败。正例仍可能因其他字段触发其他 control,这不影响本实验;真正需要拒绝的是解析错误、artifact 下载错误、unsupported 状态或目标为空后仍被包装成成功。
kubescape scan control "Privileged container" \
--format json --format-version v2 --output bad.json ./bad-pod.yaml
kubescape scan control "Privileged container" \
--format json --format-version v2 --output good.json ./good-pod.yamlKubescape 的 control 由 Rego 表达并由 OPA 求值,framework 是 controls 的集合。默认 kubescape scan 在 3.0 之后执行 overview/baseline 安全扫描,不再等于旧教程中的 NSA/MITRE framework 扫描。需要框架证据时必须写出 scan framework <framework>。官方扫描说明与framework/control 说明应作为命令和结果语义的依据。
--compliance-threshold 可让 framework/control 等视图在合规分低于阈值时返回非零退出码,但分数不是完整门禁。旧 --fail-threshold 比较的是 risk score,已经弃用,不能在迁移时直接把同一个数字改名后继续使用。演示阈值只用于验证退出码,生产判断要分别处理新增关键失败、不可评估 control、扫描覆盖率、例外到期和工具错误。一个大量低权重通过项不能抵消某个关键失败,也不能把没有节点数据的 control 计成通过。
把源码、渲染产物与 live object 接入同一条流水线
真实项目至少保存三个身份:源码提交、渲染产物摘要、集群对象的 UID 与 generation。Helm Chart 扫描默认 values 只能证明默认渲染;生产有自定义 values 时,先用与发布完全相同的 Helm 版本和参数渲染,再扫描输出目录。Kustomize 同理,应扫描实际 build 产物,而不是只扫 base。
helm template demo ./charts/demo \
--namespace posture-lab \
--values ./environments/staging/values.yaml \
> rendered.yaml
sha256sum rendered.yaml > rendered.sha256
kubescape scan framework nsa \
--format json --format-version v2 \
--output kubescape-results.json \
--use-artifacts-from ./kubescape-artifacts \
./rendered.yaml流水线产物中保留提交 SHA、Helm/Kustomize 版本、values 文件摘要、渲染摘要、CLI 与 artifact 摘要、原始 JSON 和门禁策略版本。部署后再从 API Server 导出 live object,移除 status 等动态字段后比较安全相关字段;如果准入修改、默认值或其他控制器造成差异,应将差异归到对应 owner,不能用本地报告覆盖集群事实。
对多租户仓库,不要把所有 Namespace 的结果交给每个项目组。CI 只读取本仓库脱敏输入,集群内报告按 Namespace、应用身份和职责授权。原始 workload spec 可能包含环境变量名、内部镜像路径、ServiceAccount、卷、网络和安全上下文,报告与日志本身就是敏感资产。
安装 Operator 前先审阅渲染结果与集群级权限
官方Operator 安装指南以 Helm 或 Argo CD 管理 Chart。生产接入先固定 Chart 1.40.2 这一类已评估版本,导出上游 values,再用团队 values 渲染。安装者要创建 CRD、ClusterRole/Binding、APIService、Webhook 与证书等集群级对象,通常需要平台管理员或被拆分审批的等价权限;组件运行时的 ServiceAccount 则应按固定 Chart 逐项审查,不能长期复用安装者身份。
helm repo add kubescape https://kubescape.github.io/helm-charts/
helm repo update kubescape
helm show chart kubescape/kubescape-operator --version 1.40.2
helm show values kubescape/kubescape-operator --version 1.40.2 \
> values-upstream.yaml
helm template kubescape kubescape/kubescape-operator \
--version 1.40.2 \
--namespace kubescape \
--set clusterName="<cluster-id>" \
-f values-reviewed.yaml > rendered.yaml渲染后先检查资源种类、镜像、RBAC、hostPath、capability、APIService、Webhook、Secret 引用、PVC、资源限制和外连地址。clusterName 应是稳定且不含客户或内部拓扑的资产键;集群重建后是否沿用同一键,取决于历史是否要连续,不能随意用显示名。Chart values 中 capabilities.configurationScan、continuousScan、nodeScan、vulnerabilityScan、runtimeObservability、runtimeDetection、admissionController、riskAcceptance 与 autoUpgrading 会改变部署对象、权限、数据和处理成本。
在 1.40.2 基线里,configuration scan、node scan、漏洞扫描、relevancy 与 runtime observability 等能力处于启用状态,continuous scan 与 runtime detection 等能力并非全部默认开启;autoUpgrading 保持关闭。网页片段可能与固定 Chart 不同,最终事实来自 helm template 的清单。每打开一个 capability,都重新比较 Deployment/DaemonSet、RBAC、hostPath、NetworkPolicy、CRD、外发 sink 和资源请求。
审阅通过后再安装,并检查的不只是 Pod:
helm upgrade --install kubescape kubescape/kubescape-operator \
--version 1.40.2 \
--namespace kubescape --create-namespace \
--set clusterName="<cluster-id>" \
-f values-reviewed.yaml \
--wait --timeout 10m
kubectl -n kubescape get deploy,ds,pod,pvc
kubectl get apiservice | grep -E 'kubescape|softwarecomposition'
kubectl api-resources | grep -E 'kubescape|softwarecomposition|hostdata'--wait 成功只说明 Helm 跟踪的就绪条件满足。验收还要确认预期 Linux 节点都有 DaemonSet Pod、聚合 API 的 Available condition 正常、扫描对象能读取、Storage 能写入,并能把一条 live object 结果关联回 Namespace、UID、generation、control 与 artifact 版本。
node-agent 为什么要按节点级探针治理
固定 Chart 的 node-agent 以 root 运行并使用 hostPID,即使 nodeAgent.privileged: false,模板仍可能授予 SYS_ADMIN、SYS_PTRACE、NET_ADMIN、SYSLOG、SYS_RESOURCE、IPC_LOCK、NET_RAW,并挂载 /、/var/lib/kubelet、/run、/var、内核模块、cgroup、BPF 与 debugfs 等主机路径。它还会读取 Kubernetes 工作负载与节点对象,并对部分 profile、SBOM、hostdata 与命令状态执行写操作。攻击者控制这个 Pod 时,影响面接近节点级安全传感器,而不是普通业务 sidecar。
上线前把 ServiceAccount 独占、镜像 digest/签名、RBAC diff、Pod Security 或 SCC 例外、hostPath 读写模式、runtime socket、NetworkPolicy、出口目的地和资源上限纳入评审。OpenShift 可能需要把 node-agent 映射到 privileged SCC;GKE Autopilot 则受 Workload Allowlist 约束。相同 values 在不同发行版上不等于相同权限,也不能为了让 DaemonSet 调度成功而给整个 Namespace 永久放开 privileged。
节点覆盖用“预期节点集合减去实际健康 Agent 集合”判断。下面的命令分别检查调度、授权与运行清单;预期每个应覆盖的 Linux 节点都有 Ready Pod,RBAC 与渲染基线一致。Windows 节点、内核不满足要求的节点和受托管平台限制的节点要明确标记不可覆盖,并寻找其他证据,而不是从分母删除。
kubectl -n kubescape get ds,pod -o wide
kubectl auth can-i \
--as=system:serviceaccount:kubescape:node-agent --list
kubectl -n kubescape get pod -l app=node-agent -o yaml
kubectl api-resources | grep hostdata反向实验放在一次性 Linux 节点池:禁用 host sensor,或让测试节点的 Pod Security/allowlist 阻止 node-agent 调度,再重跑依赖节点事实的 control。预期证据是缺数据、调度失败、内核或权限错误;如果报告变成 PASS,门禁必须拒绝这份结果。旧 kubescape/host-scanner 仓库已经归档,不应再独立部署;当前 Host Sensor 的实际运行对象以固定 Chart 和集群观察为准。
Storage 决定结果能否被读取,却不是历史档案库
Storage 经 Kubernetes API aggregation layer 暴露 Kubescape 对象,请求路径是 kubectl/API Server -> APIService -> Storage Service -> SQLite 元数据 + /data payload。固定 Chart 默认使用 RWO PVC,容量基线为 5Gi,并按对象种类设置队列、worker 与最大对象大小。RWO、SQLite 和文件 payload 的组合没有天然给出多副本一致性或跨区 HA,不能仅把 Deployment 副本数改成 2 就宣称高可用。
先建立四组观测:APIService condition、Storage 日志与队列、PVC 使用趋势、按 kind 的对象数量与最大对象拒绝。SBOM、ApplicationProfile、VulnerabilityManifest 等大对象会形成不同容量曲线;cleanup interval 只清理符合生命周期的对象,不保证文件系统立刻回收,也不替代历史保留策略。
一次性集群的恢复实验应先生成结果,再删除 Storage Pod:
kubectl get apiservice | grep kubescape
kubectl get --raw /apis/spdx.softwarecomposition.kubescape.io/v1beta1
kubectl -n kubescape get pvc
kubectl -n kubescape delete pod -l app=storage
kubectl get workloadconfigurationscans -A预期 Pod 重建后 APIService 恢复 Available,同一 PVC 可重新挂载,对象重新可读;若对象被定义为可再生成,也要观察重新扫描而不是假定原数据必然持久。反例可以在一次性环境用受控的小容量或临时阻断 APIService 到 Storage,保存 condition、写入错误、队列/磁盘指标与调用端错误。只看到 Storage Pod Running 不能证明 API 可用。
备份 /data 时必须一致地处理 SQLite 数据库及 WAL/SHM 和 payload 文件,不能只复制其中一个子目录。集群内结果会更新且可再生成,需要长期审计时,应导出到具备不可变身份、访问控制、保留和删除能力的外部证据仓;本地 Storage 更适合作为当前态势工作集。
provider 带来集中能力,也引入双向信任与数据责任
provider 可以为 CLI/Operator提供 framework、control 和配置,也可以接收扫描结果。Operator 中的 synchronizer 负责同步,provider 还可能通过 OperatorCommand 触发受支持操作。因此 provider 不是单向“上传看板”,而是数据通道与控制通道。ARMO Platform 属于商业 provider;开源离线模式没有自动获得它的集中 UI、历史、租户和运营能力。
接入前先从渲染清单与网络观测确认上传对象:workload spec、Namespace、镜像、RBAC、SBOM、漏洞、ApplicationProfile 和运行行为都可能含有内部身份或业务线索。评审还要确定传输域名、区域、保留、导出、删除、租户隔离、匿名化可逆性、provider 下发 artifact/命令的信任根、断网缓存和审计。商业能力、价格和区域支持以采购与产品页面为准,不写入长期不变的架构假设。
凭证使用预创建 Secret 或外部密钥系统注入,避免把 account/accessKey 写进 Git values、命令历史和 Helm release 历史。ServiceAccount 只读取本组件所需 Secret,NetworkPolicy 只开放经批准的 DNS/IP 与端口。断网实验要观察扫描是否继续、同步队列如何增长、artifact 多久过期、恢复后是否补传风暴;门禁不可把 provider 不可达降级成“没有新问题”。
退出 provider 时撤销 access key、工作负载身份和云 IAM,删除同步配置,阻断外发,再从出口日志确认没有请求;随后按合同导出或删除外部历史。只卸载 Operator 不会自动完成商业租户中的数据删除,也不会撤销外部身份。
运行时检测开启后,学习期和告警链才是真正成本
Kubescape Runtime Threat Detection 依赖 Linux eBPF,要求 Linux kernel 至少为 4.14,Windows 节点不支持。node-agent 通过 Inspektor Gadget 与相关 eBPF gadgets 观察进程、文件、网络、syscall 和 capability 等事件。固定 Chart 的 runtimeDetection 默认并非开启;启用开关也不代表默认规则和告警 sink 已经存在,安装时还要明确 alertCRD.installDefault=true 或部署经审查的 Rules 与 RuntimeRuleAlertBinding。
运行时 profile 的默认学习期为 24 小时。学习窗口没覆盖滚动重启、批处理、运维命令和低频路径时,合法行为会在观察期后成为异常。ApplicationProfile 不完整时,规则仍可能执行,因此团队要把 profile 状态、规则版本、RuleID、workload/container 身份、进程证据和 sink 投递状态放在同一条告警记录中。
正向实验只在一次性 Linux 集群开启检测,等待 profile 达到预期状态后,在测试容器启动一个学习期未出现的进程。预期 node-agent 日志或 sink 产生具体 RuleID、Pod/container、process 与 profile 状态。反例是在学习不足时执行合法发布脚本,观察误报并通过限定规则、延长或重建学习、按工作负载抑制来处理;不要为了安静直接关闭全部规则。运行时检测增加每节点 CPU/内存、内核事件与日志吞吐,还会放大 SIEM 存储成本,容量评估必须覆盖事件突发和 sink 背压。
用分层证据排查“扫描不更新”
第一层看目标:本地文件是否为空、kubeconfig context 是否正确、Namespace 排除是否误配、live object 的 UID/generation 是否变化。第二层看规则:CLI/Chart 与 artifact 版本是否匹配,framework 是否显式指定,control 输入和例外是否改变。第三层看采集:scanner 是否完成、node-agent 是否覆盖全部目标节点、Host Sensor 是否有新数据、运行时内核是否受支持。
第四层看存储:APIService condition、Service endpoints、mTLS 证书、Storage 日志、PVC、SQLite 与队列。第五层看同步:provider DNS/TLS、凭证、出口策略、积压与远端租户身份。最后才看 UI。这样的顺序能区分“没有风险”“没有扫描”“有扫描但写入失败”“本地可读但没有同步”四种完全不同的状态。
不要用单个组件 Ready 代替端到端证明。一次有效采样至少包含目标身份、观察时间、采集组件版本、artifact/control 版本、原始结果摘要、缺失数据、Storage 读取状态和 provider 接收状态。排障日志在分享前删除 Secret、Token、内部域名、环境变量值、镜像私有路径和客户信息。
容量、高可用与成本要按组件分别建模
CLI 的主要成本在流水线 CPU、规则下载、镜像/仓库读取和结果产物;并发扫描过多会同时冲击 Registry、Git、API Server 与制品缓存。Operator 的调度成本与集群对象数量和变化率有关,node-agent 则按节点、事件速率与启用 capability 消耗资源。Storage 的压力来自对象数、payload 大小、队列和清理速度,provider/OTel/SIEM 的成本来自外发流量、索引与保留。
scanner 和 operator 可通过控制器副本、选主或重试获得一定可用性,但要以具体 Chart 渲染和组件承诺为准。DaemonSet 的“HA”是每节点覆盖,不是同一节点运行多个 agent;agent 缺失必须告警。默认 Storage 的 RWO + SQLite/文件模式首先解决单实例持久工作集,故障恢复依赖 PVC 可重新调度和 API aggregation 恢复。需要跨区历史或灾备时,把不可变结果外送到专门证据仓,而不是未经验证地水平扩 Storage。
容量基线从实验负载测得:工作负载数量、镜像数量、每轮结果大小、节点事件峰值、队列等待、PVC 增长、cleanup 后稳定值和 provider 补传速率。生产阈值来自集群规模与恢复目标,不照抄演示数字。成本评审要同时计算节点探针资源、扫描 Job 峰值、PVC、外部历史仓、SIEM 日志与商业座席,避免“开关免费、证据链昂贵”的预算缺口。
升级与回滚要把 Chart 当成多组件兼容事务
升级前保存 release values、manifest、CRD/APIService、RBAC、PVC 与关键对象样本,再渲染目标 Chart 做 diff。重点检查 capability 默认值、弃用字段、镜像与组件组合、CRD/API 兼容、Storage schema/文件布局、mTLS 证书、node-agent 主机访问、资源 request/limit 和 provider 协议。不要单独把某个镜像换成仓库最新版,Chart 锁定的是一组经过组合的组件版本。
helm list -n kubescape
helm get values kubescape -n kubescape -a > values-before.yaml
helm get manifest kubescape -n kubescape > manifest-before.yaml
helm show values kubescape/kubescape-operator --version <target-chart> \
> values-target.yaml
helm template kubescape kubescape/kubescape-operator \
--version <target-chart> -n kubescape \
-f values-reviewed.yaml > manifest-target.yaml
helm upgrade kubescape kubescape/kubescape-operator \
--version <target-chart> -n kubescape \
-f values-reviewed.yaml --wait --timeout <budget>升级后比较同一组测试对象的 control ID、不可评估项、节点覆盖、APIService、Storage 读写和 provider 同步,不能直接比较两个版本的总分。Helm history 仍在且数据向后兼容时可 helm rollback;CRD、SQLite schema、payload 或外部 provider 状态发生不可逆迁移时,Helm 回滚不会自动恢复数据。真正的回滚合同必须包含旧 Chart、旧 values、CRD 兼容结论、Storage 恢复点和外部同步处理。
完整退出要清掉集群对象、节点权限与外部数据
先停止新扫描和外发,导出需要保留的原始结果与身份索引,撤销 provider 凭证,再卸载 Helm release。删除 Namespace 前确认 Storage 数据、告警与历史已经按策略处理。CRD 删除会级联删除对象,不能把它当普通清理命令直接复制到共享集群。
helm uninstall kubescape -n kubescape
kubectl api-resources | grep -E 'kubescape|softwarecomposition|hostdata'
kubectl get crd,apiservice,clusterrole,clusterrolebinding,pvc -A \
-o name | grep -E 'kubescape|softwarecomposition|hostdata'第二条检查故意保留结果输出:它用于建立残留清单,不代表可以批量删除。逐项处理 CRD 及其对象、APIService、ClusterRole/Binding、Webhook、SCC/allowlist、PVC、ApplicationProfile、NetworkNeighborhood、SBOM、VulnerabilityManifest、Prometheus/Alertmanager/SIEM 规则和 OTel 配置。Namespace 卡在 Terminating 时先找 finalizer、APIService 与 PVC 归属,不强删控制面记录让外部资源失去引用。
最后从三个方向证明退出:集群里没有 node-agent 和高权限 RBAC,出口日志里没有 synchronizer/provider 请求,外部 provider 中的凭证、租户数据和保留任务已按合同关闭。CLI 分发也要从开发镜像、Runner、插件管理器与缓存中撤销旧版本;规则 artifacts 和历史报告按审计保留策略处理。完成这些动作后,Kubescape 才从一条仍可能采集、存储和外发安全数据的系统,真正变成可追溯的历史工具。
