# 借助 Shell Operator 监控集群中的镜像漏洞

在[使用 Shell-Operator，让 Pod 继承节点标签](https://blog.fleeto.us/post/copy-labels-from-node-to-pod-by-shell-operator/)一文中，展示了使用 [Shell Operator](https://github.com/flant/shell-operator) 在创建工作负载时从对应节点拷贝指定标签的方法。Shell Operator 还有个功能就是生成 Prometheus Metrics，本文就是从这个角度出发，用 Shell Operator 定时调用 [Trivy](https://github.com/aquasecurity/trivy) 扫描指定命名空间下的所有在用镜像，并输出指标给 Prometheus，最终可以进行展示和告警。

Shell Operator 除了上次提到的根据 Kubernetes 事件进行触发之外，还包含了在启动时和定期运行两种触发方式，这里我们用定时触发的方式：

~~~yaml
configVersion: v1
schedule:
- name: "scan images each 5 min"
  crontab: "*/5 * * * *"
  allowFailure: true
~~~

这里我定义了一个 5 分钟触发一次的任务。镜像接受一个缺省为 `NS_LABEL="trivy=true"` 的标签，每次触发时，会根据这个标签选择命名空间，获取选定命名空间中运行的镜像，并调用 `trivy i -f json [image]` 输出结果，将结果转换为 Prometheus 指标进行输出。

~~~dockerfile
FROM flant/shell-operator:v1.0.0-rc.2
ENV TRIVY_CACHE_DIR=/data/trivy \
    TRIVY_QUIET=true \
    GITHUB_TOKEN="" \
    NS_LABEL="trivy=true"
RUN apk --no-cache add python3=3.8.8-r0
COPY trivy-scanner.py /hooks
COPY trivy /usr/local/bin
VOLUME [ "/data/trivy", "/data/cache" ]
~~~

上文中的几个环境变量：

- TRIVY_CACHE_DIR：指定 Trivy 缓存目录，可以在 Kubernetes 中把 Trivy 工作目录持久化。
- TRIVY_QUIET：Trivy 环境变量，指定 Trivy 用静默方式运行。
- GITHUB_TOKEN：Trivy 会使用该 Token 更新漏洞库，防止触发 Rate Limit。
- NS_LABLE：仅对符合标签的命名空间进行扫描。

在脚本中执行的任务还是很简单的三部曲：

- kubectl get ns
- kubectl get po
- trivy

可以看到这里需要列表查看 Namespace 和 Pod 对象，因此我们的 RBAC 需要进行相应的配置，例如设置 ClusterRole：

~~~yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: trivy-scanner
rules:
- resources:
  - pods
  - namespaces
  verbs:
  - get
  - watch
  - list
~~~

> 实际上也可以通过之前用到的 Kubernetes 事件进行触发。因此这里还保留了 watch 事件。

Deployment 需要注意一点是，Shell Operator 使用 9115 端口的 `/metrics` 开放指标数据供 Prometheus 抓取，因此需要使用 `annotations` 对此进行标注。

使用 Trivy 获取到 JSON 格式出具的扫描结果之后，就可以利用 Shell Operator 的指标输出能力，把数据整理为如下格式：

~~~json
{"name":"so_vulnerabilities","set":0,"labels":{"image":"dustise/sleep:v0.9.6","severity":"UNKNOWN"}}
{"name":"so_vulnerabilities","set":1,"labels":{"image":"dustise/sleep:v0.9.6","severity":"HIGH"}}
~~~

每行一条记录，输出到 `$METRICS_PATH` 环境变量指定的文本文件中，Shell Operator 会把它转换为 Prometheus 指标：

~~~plaintext
so_vulnerabilities{hook="trivy-scanner.py",image="dustise/sleep:v0.9.6",severity="CRITICAL"} 1
so_vulnerabilities{hook="trivy-scanner.py",image="dustise/sleep:v0.9.6",severity="HIGH"} 11
so_vulnerabilities{hook="trivy-scanner.py",image="dustise/sleep:v0.9.6",severity="LOW"} 2
so_vulnerabilities{hook="trivy-scanner.py",image="dustise/sleep:v0.9.6",severity="MEDIUM"} 9
so_vulnerabilities{hook="trivy-scanner.py",image="dustise/sleep:v0.9.6",severity="UNKNOWN"} 0
~~~

接下来就可以在 Prometheus 中检查输出内容，出具监控图表或者制定告警规则了。
