Cyrus Blog

FLAG{S0_H4PPY_C_U_H3R3} (>.<)

Clair scanner 源码分析和二次开发

本文共 1k 字,预计阅读时间 3 分钟。

本文是对 Clair 开源客户端 Clair scanner 源码分析,而非对 Clair 本身(Server 端)进行分析。关于 Clair 本身我推荐看看这个

利用 docker-compose 可以快速构建 Clair 服务端,而 Clair scanner 则是一个简单易用的客户端,我们可以基于此开发一系列自己需要的功能。这里本文 Clair scanner 源码结构进行简单分析,介绍一些编译过程中可能会遇到的小问题,但是出于工作内容原因不会给出二次开发的案例。

源码编译

二次开发自然需要从源码编译,然而源码年久失修并且并未考虑到一些报错,文章开头我们给出一些解决方案。毕竟至少先要跑起来!

移除 clair/api/v1 依赖

clair.go 中有一个名为 github.com/coreos/clair/api/v1 的包被引用,然而这个包非常难以下载和配置。经过研究这个包中只有一些简单地 struct 定义,所以不妨手写一下,移除依赖。

新建文件 api.go 如下:

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
30
31
32
33
34
35
36
package main

type newerLayerFeaturesVulnerability struct {
Name string
NamespaceName string
Description string
Link string
Severity string
FixedBy string
}

type newerLayerFeature struct {
Name string
NamespaceName string
VersionFormat string
Version string
AddedBy string
Vulnerabilities []newerLayerFeaturesVulnerability
}

type newerLayer struct {
Name string
Path string
ParentName string
Format string
Features []newerLayerFeature
}

type newerLayerEnvelopeError struct {
Message string
}

type newerLayerEnvelope struct {
Layer newerLayer
Error *newerLayerEnvelopeError
}

然后对 clair.go 做出以下修改,这里给一个 git diff 截图:

之后正常通过 go get -v 下载依赖之后应该可以正常编译了,然而别急,大概率是跑不了的。

Docker API 版本修正

大概率跑起来之后会出现一个 Could not create a Docker client 的错误,这是因为 Docker API 版本过高造成的。实际使用中我没有发现各版本的差别(除了会报错),因为我们可以简单地在终端里执行:

1
export DOCKER_API_VERSION="1.39"

如果依旧报错,改为 1.38 版本完全可以。

也可以选择在 docker.go 中作如下修改。注意,这样的修改在 zsh 中可以正常工作,但是 bash 不可以。bash 中请继续每次使用 export。

IP 配置

Clair scanner 的 -c (--clair)--ip 两个 IP 地址需要选择。这里做一个简单的解释 。

如果你的 Clair 服务端跑在本地,这里保持 localhost 就行,不用使用这两个选项。

如果你的 Clair 服务端跑在 http://A.A.A.A:6060,clair-scanner 所处 IP 为 B.B.B.B,那么你的配置应该如下:

1
-c "http://A.A.A.A:6060" --ip "B.B.B.B"

这里要求服务端可以通过 B.B.B.B 访问到 clair-scanner 所处位置,所以我们推荐将客户端跑在本地虚拟机中而非 VPS,因为这样没有公网 IP 的我们会无法使用。我们稍后对此解释。

交互流程

因为 Clair 服务端需要通过 Clair scanner 启动的 Web 服务来获取 Layer 文件,所以要保证 Clair scanner 所位于的 IP 是可(内网或外网)访问的。这就是为什么我们为什么推荐在虚拟机而不是 VPS 启动服务端的原因。

主要函数模块

docker.go

  • createDockerClient 新建 Docker 通信会话
  • saveDockerImage 保存 Docker 镜像到本地,并解压出 tar 包
  • readManifestFile 获取镜像的 Manifest
  • getImageLayerIds 通过 Manifest 获取每个 Layer 的 ID

server.go

  • 在 9279 端口启动一个 Web 文件服务

chair.go

  • analyzeLayer 提交一个 Layer 分析请求
  • analyzeLayers 调用上面的函数,提交多个请求
  • fetchLayerVulnerabilities 提交一个获取 Layer 扫描结果的请求
  • getVulnerabilities 调用上面的函数,获取最后一层的结果,因为最后一层包含前面全部层次的漏洞

scanner.go

  • scan 扫描镜像【可在这里对整体流程就行修改,添加多镜像扫描等】
  • 剩余模块可以自行跟进 scan 函数【最低可在 Layer 粒度处理数据】

reporter.go

  • 主要是一些报告输出模块【二次开发后可以自定义输出结果】