在集群内持续扫描工作负载并把结果写成CRD的Kubernetes Operator
trivy-operator 是 Aqua 开源的集群内安全扫描 Operator,把 Trivy 的扫描能力从「手工跑一次」变成持续自动执行,并把每一次结果固化成 Kubernetes 对象。
理解它只要抓住一句话:trivy k8s 是 CLI 的即时扫描,trivy-operator 是把同一套扫描搬进集群、由控制器按需触发、结果落到 CRD 里。
两者的分工:
trivy k8s 命令行即时扫描,人盯着看,结果在终端里
trivy-operator 常驻控制器,持续扫描,结果成为可查询、可告警、可归档的 API 对象
在这批工具里的分工:
kube-bench CIS 基线,节点与进程配置
kubescape 框架合规(NSA/CISA、MITRE),也有集群内 Operator
kube-linter 清单 lint,纯客户端
trivy 命令行漏洞与配置扫描
trivy-operator 集群内持续扫描,结果写成 CRD —— 本页
历史沿革需要知道:这个项目的前身是 Starboard(Aqua 2022-05 宣布停掉 Starboard 并并入 Trivy 家族)。当时的 starboard-operator 更名成了 Trivy Operator,Starboard CLI 则演变为今天的 trivy k8s。老仓库 aquasecurity/starboard 未归档,但描述里写着「Superseded by trivy-operator」。
文档地址是个坑:官方文档在 aquasecurity.github.io/trivy-operator/,不在 trivy.dev 上。 trivy.dev/latest/docs/operator/ 会 404 —— trivy.dev 的文档侧栏里根本没有 Operator 章节。查资料时直接去前者。
当前状态:活跃维护,最新版本 v0.34.0(2026-08-24),基本按月发布。README 声明项目仍处于 incubating 阶段,「some APIs and Custom Resource Definitions may change」,没有跨版本稳定性承诺。
# Helm(先加仓库)
helm repo add aqua https://aquasecurity.github.io/helm-charts/
helm repo update
helm install trivy-operator aqua/trivy-operator \
--namespace trivy-system --create-namespace
# Helm OCI(需要 Helm >= 3.8.0)
helm install trivy-operator oci://ghcr.io/aquasecurity/helm-charts/trivy-operator \
--namespace trivy-system --create-namespace
# 静态 YAML
kubectl apply -f https://raw.githubusercontent.com/aquasecurity/trivy-operator/<TAG>/deploy/static/trivy-operator.yaml
# 确认部署成功
kubectl get deployment -n trivy-system
几个必须知道的默认值:
命名空间 trivy-system(静态 YAML 会创建它)
扫描范围 除 kube-system 与 trivy-system 之外的所有命名空间
卸载静态 YAML 会连带删除所有已生成的安全报告以及 CRD。 卸载前先把要留的报告导出。
trivy-operator 没有 CLI,所有操作通过 kubectl 与 helm 完成。
# 查看各类报告
kubectl get vulnerabilityreports,configauditreports,exposedsecretreports -A
kubectl get rbacassessmentreports,infraassessmentreports,sbomreports -A
kubectl get clustercompliancereports
kubectl get clustervulnerabilityreports,clustersbomreports
# 看某一份报告的详情
kubectl get vulnerabilityreport -n default -o yaml
kubectl describe configauditreport -n default <名称>
CRD 的短名:vulns / vuln、configaudit / configaudits、exposedsecret / exposedsecrets 等。
API 组与版本是 aquasecurity.github.io/v1alpha1。实际随 chart 发布的 CRD 共 12 个:
命名空间级
VulnerabilityReport 工作负载镜像的 CVE 结果
ConfigAuditReport 工作负载/资源配置审计
ExposedSecretReport 镜像里暴露的密钥
RbacAssessmentReport Role / RoleBinding 的 RBAC 评估
InfraAssessmentReport 基础设施类资源评估
SbomReport 软件物料清单
集群级(Cluster 前缀)
ClusterVulnerabilityReport 控制平面与节点组件的 CVE
ClusterConfigAuditReport
ClusterRbacAssessmentReport
ClusterInfraAssessmentReport
ClusterSbomReport
ClusterComplianceReport 按合规框架汇总的结果
注意官方文档的 CRD 表格并不完整:它漏掉了 ClusterComplianceReport、ClusterVulnerabilityReport、ClusterSbomReport、ClusterConfigAuditReport 四个(它们确实随 chart 发布),还把 exposedsecretreports 拼成了 exposedsecretsreports。以仓库的 deploy/helm/crds/ 目录为准。 ClusterComplianceDetailReport 有文档页但没有 CRD 文件,属于旧版遗留,已被 ClusterComplianceReport 取代。
这里要纠正一个常见误解:不存在一个叫「聚合报告开关」的 Helm 值。 聚合是通过 Cluster 前缀的 CRD 实现的,而它们的触发条件各不相同:
ClusterVulnerabilityReport 由 ClusterSbomReport 派生(ownerReferences 指向它)
要求 operator.sbomGenerationEnabled 为 true(默认 true)
values.yaml 原文:required for enabling ClusterVulnerabilityReports
注意:它只支持原生 Kubernetes / RKE2,托管云厂商的集群不适用
ClusterComplianceReport 由 compliance.* 一组值控制,见下
ClusterSbomReport 体积可观,是 etcd 存储的主要压力来源。下游不少封装发行版会主动把 operator.sbomGenerationEnabled 设为 false,除非确实有人消费 ClusterVulnerabilityReport。
compliance.specs 要生成哪些合规报告
compliance.cron 重新生成的周期,默认 0 */6 * * *(每 6 小时)
compliance.reportType summary 或 all
compliance.failEntriesLimit 逐条明细的数量上限,默认 10
Helm 默认创建的合规规格是:
k8s-cis-1.23
k8s-nsa-1.0
k8s-pss-baseline-0.1
k8s-pss-restricted-0.1
values.yaml 里还注释着 eks-cis-1.4 与 rke2-cis-1.24 可供开启。
注意裸名字 nsa / cis 与 CR 的实际名字不是一回事。 文档的「内建报告」表里写的是 nsa、cis,而 Helm 真正创建出来的对象名是 k8s-nsa-1.0、k8s-cis-1.23。pss-baseline / pss-restricted 同理,实际是带版本号的 k8s-pss-baseline-0.1 / k8s-pss-restricted-0.1。
kubectl get clustercompliancereports
kubectl get clustercompliancereport k8s-nsa-1.0 -o yaml
# 扫描器开关(默认全开)
operator.vulnerabilityScannerEnabled / configAuditScannerEnabled
operator.rbacAssessmentScannerEnabled / infraAssessmentScannerEnabled
operator.exposedSecretScannerEnabled / clusterComplianceEnabled
# 扫描范围
operator.targetNamespaces 默认空 = 全部
operator.excludeNamespaces
operator.targetWorkloads 默认 pod,replicaset,replicationcontroller,
statefulset,daemonset,cronjob,job
# 调度与并发
operator.scanJobTimeout 默认 5m
operator.scanJobsConcurrentLimit 默认 10 —— 注意名字,不是 concurrentScanJobsLimit
operator.scanJobsRetryDelay 默认 30s
operator.scanNodeCollectorLimit 默认 1
# Trivy 自身
trivy.severity 默认 UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL
trivy.ignoreUnfixed 默认 false
trivy.timeout 默认 5m0s
trivy.mode Standalone 或 ClientServer
trivy.resources requests 100m/100M,limits 500m/500M
trivy.image.* 默认 mirror.gcr.io / aquasec/trivy / 0.74.0
# 报告生命周期
operator.scannerReportTTL 默认 24h —— 报告 24 小时后被删除!
operator.scanJobTTL 默认空 = 不清理
alternateReportStorage.enabled 默认 false;true 则报告写 PVC 而不是 CRD
# 压缩扫描 Job 日志(注意作用域不是 operator.*)
trivyOperator.scanJobCompressLogs 默认 true
operator.scanJobCompressLogs 这个名字在旧资料里很常见,但在 chart 0.36.0 里 它已经不在 operator. 下面了,正确路径是 trivyOperator.scanJobCompressLogs。operator.concurrentScanJobsLimit 同样是错的,应为 operator.scanJobsConcurrentLimit。
默认 报告作为 CRD 存在 etcd 里,数量 = 工作负载数 × 扫描次数
所以在大集群上对象数量会持续增长,是 etcd 容量的真实压力来源
TTL operator.scannerReportTTL 默认 24h,会给报告打上注解
trivy-operator.aquasecurity.github.io/report-ttl
控制器据此重新排队并在过期后清除,启动时也会扫一遍
单份报告可以改这个注解来覆盖全局值
替代存储 alternateReportStorage.enabled=true 把报告写到 PVC 上的 JSON 文件
控制器还利用 Kubernetes 的垃圾回收机制做失效与重扫:删除一个 ReplicaSet 会连带删除它的 VulnerabilityReport;删除被 ReplicaSet 拥有的报告会触发重扫并重建。配置变更同样会触发重扫 —— 插件配置 ConfigMap 的内容被哈希后以 plugin-config-hash 标签写在 ConfigAuditReport 上,哈希对不上就删除旧报告重新扫描。
# 扫描工作负载镜像时用到的拉取凭据
operator.privateRegistryScanSecretsNames 形如 namespace: "secret1,secret2"
operator.accessGlobalSecretsAndServiceAccount 默认 true
# Trivy 镜像自身 / 漏洞库
trivy.image.registry / repository / tag / imagePullSecret
trivy.dbRegistry / trivy.dbRepository 默认 mirror.gcr.io / aquasec/trivy-db
trivy.javaDbRegistry / trivy.javaDbRepository
trivy.registry.mirror
已知限制:operator 不支持同时使用多个仓库的不同凭据。 需要认证时,trivy-db 与 trivy-java-db 必须来自同一个仓库,认证用的是 trivy.dbRepository 上配的那个。
气隙环境还需要 trivy.offlineScan: true(禁止一切出站 HTTP 请求)、trivy.useEmbeddedRegoPolicies: true(用镜像内嵌的 Rego 策略,默认开启,与 trivy.useBuiltinRegoPolicies 互斥)、trivy.clientServerSkipUpdate: true 与 trivy.skipJavaDBUpdate: true,以及按需设置 operator.httpProxy / trivy.httpProxy / httpsProxy / noProxy。
配置除了走 Helm values,还可以落到 ConfigMap/Secret 里:trivy-operator-trivy-config 是主配置(配套同名 Secret 放 token)。用 operator.valuesFromConfigMap / trivy.valuesFromConfigMap(以及 valuesFromSecret)能让它们覆盖 Helm 值。
Operator 通过 Service 暴露 /metrics,可与 Prometheus 对接:
trivy_image_vulnerabilities VulnerabilityReport 中各状态的计数
trivy_resource_configaudits ConfigAuditReport 的检查计数
trivy_resource_infraassessments InfraAssessmentReport 的检查计数
trivy_role_rbacassessments RbacAssessmentReport 的检查计数
trivy_image_exposedsecrets ExposedSecretReport 的检查计数
trivy_cluster_compliance 合规状态,带 description / status / title 标签
还有一批逐条明细的指标(trivy_configaudits_info、trivy_vulnerability_id 等)默认关闭,因为它们会显著放大指标基数。需要时通过 metricsConfigAuditInfo、metricsVulnIdEnabled 等开关打开。
给报告指标加业务标签:设 trivyOperator.reportResourceLabels: "owner,app" 后,指标上会出现 k8s_label_owner="platform" 这类标签;前缀由 trivyOperator.metricsResourceLabelsPrefix 控制,默认 k8s_label_。
trivy.dev/latest/docs/operator/ 直接 404,官方文档在 aquasecurity.github.io/trivy-operator/。同样地,aquasecurity.github.io/trivy-operator/latest/docs/settings/ 也 404,设置项以仓库里的 docs/settings.md 为准。operator.scannerReportTTL 默认 24h。很多人装完隔天回来发现报告全没了,以为出了故障 —— 这是设计行为。要做长期趋势分析,要么调大这个值,要么开 alternateReportStorage,要么把指标接进 Prometheus。operator.sbomGenerationEnabled 等具体开关,不要去 values.yaml 里找一个不存在的总开关。operator.concurrentScanJobsLimit 的正确名字是 operator.scanJobsConcurrentLimit;operator.scanJobCompressLogs 的正确路径是 trivyOperator.scanJobCompressLogs。照旧文章写会被 Helm 静默忽略(不报错,但也不生效)。operator.scanJobsConcurrentLimit(默认 10)和 trivy.resources。trivy.severity 默认什么级别都报。默认值是 UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL,开箱即用的输出会很吵。生产上通常收敛到 HIGH,CRITICAL。allowPrivilegeEscalation: false、drop: ALL、readOnlyRootFilesystem: true,是不折不扣的加固配置。但把 trivy.command 改成 filesystem 或 rootfs 时它必须以 root 运行(values.yaml 里明确写了 runAsUser: 0)。所以「这个 Operator 需要特权」的说法只在特定模式下成立。/var/lib/etcd、/var/lib/kubelet、/etc/kubernetes、/etc/cni/net.d/、/etc/systemd、/lib/systemd/ 等路径来读节点组件信息,在严格启用 Pod Security Admission 的命名空间里会直接起不来。可用 nodeCollector.excludeNodes 跳过节点,用 operator.scanNodeCollectorLimit(默认 1)限制并发。ClusterVulnerabilityReport 在托管 Kubernetes 上不工作。它只支持原生 Kubernetes 与 RKE2;EKS、GKE、AKS 这类托管集群拿不到有意义的结果。ClusterSbomReport 会显著撑大 etcd。SBOM 对象很大,而它又是 ClusterVulnerabilityReport 的前置条件。不需要节点组件漏洞视图时,把 operator.sbomGenerationEnabled 关掉能省下大量存储。helm install --version 填的是 chart 版本,别把两个数字搞混。kubectl apply 新的 CRD。trivy-db 与 trivy-java-db 必须来自同一仓库,不支持给不同仓库配不同凭据。trivy k8s 的结果不会完全一致。CLI 扫描有 node-collector 与其他采集路径,Operator 的采集范围由 targetWorkloads、targetNamespaces 等值决定。两边的数字对不上时,先核对扫描范围而不是怀疑哪边算错了。trivy — 命令行扫描,与 Operator 共用同一套引擎kubescape — 框架合规扫描,也有集群内 Operatorkube-bench — CIS 基线,面向节点kube-linter — 清单 lint,纯客户端popeye — 集群现状只读体检kubectl — Kubernetes集群管理工具