zhangguanzhang's Blog

开源容器镜像扫描 trivy 懒人重点指南

字数统计: 1.3k阅读时长: 5 min
2024/04/22

写给那些不爱看文档和自己使用中应该注意的一些技巧

关于

介绍下 trivy 的使用和一些注意的点

介绍和安装

trivy 是一个漏洞安全扫描,可以扫描

  • Container Image
  • Filesystem
  • Git Repository (remote)
  • Virtual Machine Image
  • Kubernetes
  • AWS

因为是 golang 开发的,安装非常简单 release 页面找到最新的 release,点击 Show all xx aseets 后下载 trivy_x.xx.x_Linux-64bit.tar.gz 下来解压即可。

使用

trivy 的 cmd 和 option 是使用 cobra 框架写的,支持子命令和全局选项,可以trivy -htrivy image -h 依次查看对应的命令帮助。

扫描示例:

1
2
3
4
$ trivy image debian:12
$ trivy image --input debian-12.tar.gz
$ trivy repo https://github.com/knqyf263/trivy-ci-test
$ trivy k8s --report summary cluster

可以自行研究下命令参数和看官方文档,这里不写详细的,很多还是要看官方文档。

trivy 在执行扫描的时候,会下载两个 db文件:

  • ghcr.io/aquasecurity/trivy-db
  • ghcr.io/aquasecurity/trivy-java-db

该文件是 oci(application/vnd.oci.image.manifest.v1) 镜像格式的,同时是存储在 ghcr 的 github 镜像仓库上的,国内可能能拉取到,更大可能是拉取不到。可以使用国内的同步源:

  • m.daocloud.io/ghcr.io/aquasecurity/trivy-db
  • m.daocloud.io/ghcr.io/aquasecurity/trivy-java-db

命令行参数为:

1
2
3
trivy image --db-repository m.daocloud.io/ghcr.io/aquasecurity/trivy-db \
--java-db-repository m.daocloud.io/ghcr.io/aquasecurity/trivy-java-db \
debian:12

cve 准确性

我们内部使用扫描后,客户现场扫描的报告,查看了结果两者不是一样的,属于两者结果有部分交集的结果。查看官方文档 vulnerability 章节说明了前面俩 db 的数据来源,并不是比较全的 cve 列表,同时 trivy-db 是每隔 6 个小时构建更新的

内部 ci 上使用

先得解决机器无法拉取 github 镜像仓库,需要配同步到内网镜像仓库上比较好,另外 oci 格式是不支持 docker pull 的,可以使用 skopeo 的 sync 命令每天定时间隔同步:

1
2
3
4
5
6
7
8
9
10
declare -a list=(
"trivy-db:2"
"trivy-db:1"
trivy-db
"trivy-java-db:1"
)
for image in ${list[@]};do
skopeo --insecure-policy copy docker://m.daocloud.io/ghcr.io/aquasecurity/${image} docker://xxx.com/ci-run/${image}

done

根据官方文档 trivy_server 支持 client/server 模式的,server 是一直运行的:

1
2
trivy server
trivy image --server http://localhost/remote:4954 alpine

对于我们好多节点还有地域性质的 jenkins 构建场景不适合,同时每个单独运行的话,由于 db 默认是下载后解压到 ~/.cache/trivy 下的文件,默认的 --cache-backend 是 fs,多 jenkins 构建机器的话,会每次运行都下载很浪费容量。

查看了官方文档 cache 支持 redis 作为缓存的,但是从 -h 选项和文档里示例的都是 tls 的 redis 链接方式,对于密码方式就不支持,然后查看源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// https://github.com/aquasecurity/trivy/blob/63c9469bdd91ee71ee643862329a3948b42c561d/pkg/commands/operation/operation.go#L43-L69
func NewCache(c flag.CacheOptions) (Cache, error) {
if strings.HasPrefix(c.CacheBackend, "redis://") {
log.Info("Redis cache", log.String("url", c.CacheBackendMasked()))
options, err := redis.ParseURL(c.CacheBackend)
if err != nil {
return Cache{}, err
}

if !lo.IsEmpty(c.RedisOptions) {
caCert, cert, err := GetTLSConfig(c.RedisCACert, c.RedisCert, c.RedisKey)
if err != nil {
return Cache{}, err
}

options.TLSConfig = &tls.Config{
RootCAs: caCert,
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
}
} else if c.RedisTLS {
options.TLSConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
}
}

redisCache := cache.NewRedisCache(options, c.CacheTTL)
return Cache{Cache: redisCache}, nil
}

发现 redis 的 client 库是使用的 go-redis,查看它的官方文档,发现可以 DSN 形式传入密码:

1
opt, err := redis.ParseURL("redis://<user>:<pass>@localhost:6379/<db>")

同时,不推荐命令行带参数,不然会泄露密码,参考官方文档 config-file 使用配置文件:

1
2
3
4
5
db:
repository: m.daocloud.io/ghcr.io/aquasecurity/trivy-db
java-repository: m.daocloud.io/ghcr.io/aquasecurity/trivy-java-db
cache:
backend: "redis://:xxx@redis-server:6379"

一开始报错 noauth,后面发现必须加冒号,没有用户名就是上面这样的格式,同时官方文档 configuration 说明了,对于每一个长选项,都可以使用环境变量代替,例如:

1
2
trivy --config xxx.yml xxx
TRIVY_CONFIG=xxx.yml trivy xxx

这样 ci 里运行也不用带 cmdline 泄露配置文件路径。当然还支持模板和 output format 以及设置 --exit-code 会在 ci 里很实用。避免扫描的时候下载缓存失败导致扫描结果没出来,可以扫描之前,执行下:

1
2
3
4
5
6
7
8
9
until trivy image --download-db-only; do
echo "Command failed. Retrying..."
sleep 1
done

until trivy image --download-java-db-only; do
echo "Command failed. Retrying..."
sleep 1
done

jenkins 里,扫描可以使用官方的 html.tpl 模板,最后用 publishHTML 展示。由于 trivy 不支持一次扫描多个镜像,可以把官方的模板文件下载后分割成三部分拼接:

1
2
3
4
5
6
7
8
9
10
cp "${tpl_dir}/trivy-html-head.tpl" report.html
while read -r line; do
if [ -f "$line" ];then
trivy image --format template --template "@${tpl_dir}/trivy-html-body.tpl" -o report.tmp --input $line
cat report.tmp >> report.html
# break
fi
done < "/dev/stdin"
rm -f report.tmp
cat "${tpl_dir}/trivy-html-end.tpl" >> report.html

参考

CATALOG
  1. 1. 关于
  2. 2. 介绍和安装
    1. 2.1. 使用
  3. 3. cve 准确性
  4. 4. 内部 ci 上使用
  5. 5. 参考