镜像扫描结构图
方式2的具体操作步骤
clair是什么?
clair是一个开源项目,用于静态分析appc和docker容器中的漏洞。
漏洞元数据从一组已知的源连续导入,并与容器映像的索引内容相关联,以生成威胁容器的漏洞列表。
clair版本选择
clair选择2.0.1版本
clair安装过程
docker方式
1.clair将漏洞元数据存储在Postgres中,先拉取postgres:9.6
docker pull postgres:9.6
2.使用官方镜像clair:v2.0.1,并进行拉取
docker pull quay.io/coreos/clair:v2.0.1
3.创建clair的配置文件存放的文件夹
mkdir /root/clair_config
4.下载clair的配置文件,主要是对数据库连接方式的配置
curl -L https://raw.githubusercontent.com/coreos/clair/v2.0.1/config.example.yaml -o /root/clair_config/config.yaml
5.运行postgres容器
docker run -d -e POSTGRES_PASSWORD="" -p 5432:5432 postgres:9.6
6.运行clair容器
docker run --net=host -d -p 6060-6061:6060-6061 -v /root/clair_config:/config quay.io/coreos/clair:v2.0.1 -config=/config/config.yaml
注:此时clair容器不断从各个数据站点下载漏洞数据源并存放至数据库中,非常耗时,存在大量数据站点无法请求的情况。
若此时漏洞元数据可以下载,可继续以下步骤
7.安装辅助工具clairctl
clairctl一个轻量级命令行工具,用于在本地使用clair并生成HTML报告。
Clairctl需要go语言支持
7.1 下载go支持包
https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz
7.2 解压
tar -zxvf go1.8.3.linux-amd64.tar.gz /usr/local/go
7.3 设置环境变量
export PATH=$PATH:/usr/local/go/bin
export GOROOT=/usr/local/go
7.4 确认go版本
go version
7.5 安装clairctl
curl -L https://raw.githubusercontent.com/jgsqware/clairctl/master/install.sh | sh
7.6 确认clairctl版本
clairctl version
7.7 检查clairctl与Clair的连接
clairctl health
如果显示✔,则说明clairctl和clair能够进行正常连接。
8.上传镜像至clair
clairctl push -l nginx
9.分析本地镜像
clairctl analyze -l nginx
在分析时生成详细的分析文档
clairctl analyze -l --log-level debug nginx
10.生成结果
如果希望堪当更加详细的信息,可以使用report命令
clairctl report -l nginx
11.查看clair分析的具体步骤
clairctl analyze -l --log-level debug nginx
备注:
192.168.2.186 root Beyond#11 clair用的是最新版,也可以使用v2.0.1版本(需要等待2、3个小时让漏洞元下载下来)
目前这种方式可以成功push与analyze nginx,但查不出任何漏洞(要么nginx本身就没有漏洞,要么就是没有进行比对)
push其他镜像时,出现了问题,该问题暂不知道解决方式。
docker-compose方式
1.安装docker-compose
curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-compose-uname -s
-uname -m
-o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
检查版本
docker-compose --version
2.将以下代码保存在/root目录下的docker-compose.yml中
version: '2.1'
services:
postgres:
image: postgres:9.6
restart: unless-stopped
volumes:
- ./data/postgres/data:/var/lib/postgresql/data:rw
environment:
- POSTGRES_PASSWORD=password
- POSTGRES_USER=clair
- POSTGRES_DB=clair
clair:
image: quay.io/coreos/clair:v2.0.1
restart: unless-stopped
volumes:
- ./data/clair/config/:/config/:ro
- ./data/clair/tmp/:/tmp/:rw
depends_on:
postgres:
condition: service_started
command: [--log-level=debug, --config, /config/config.yml]
clairctl:
image: jgsqware/clairctl:latest
restart: unless-stopped
environment:
- DOCKER_API_VERSION=1.24
volumes:
- ./data/clairctl/reports/:/reports/:rw
- /var/run/docker.sock:/var/run/docker.sock:ro
depends_on:
clair:
condition: service_started
3.将以下配置代码保存在/root/data/clair/config目录中的config.yml中
clair:
database:
type: pgsql
options:
source: postgresql://clair:password@postgres:5432/clair?sslmode=disable
cachesize: 16384
api:
port: 6060
healthport: 6061
timeout: 900s
updater:
interval: 2h
notifier:
attempts: 3
renotifyinterval: 2h
4.在docker-compose.yml的路径下构建镜像并启动容器
docker-compose up -d
5.因为clairctl的官方镜像中的用户限定为clairctl,因此更改权限
chmod 777 /var/run/docker.sock
6.确认docker-compose启动的容器信息
docker-compose ps
7.确认clairctl与clair的连接状况
docker-compose exec clairctl clairctl health
8.将本地镜像push到clair
docker-compose exec clairctl clairctl push -l nginx
9.分析镜像
docker-compose exec clairctl clairctl analyze -l nginx
10.生成HTML报告
docker-compose exec clairctl clairctl report -l nginx
调用clair的api方式
先看clair v2.0.1的api文档
示例请求
POST http://localhost:6060/v1/layers HTTP/1.1
{
"Layer": {
"Name": "523ef1d23f222195488575f52a39c729c76a8c5630c9a194139cb246fb212da6",
"Path": "https://mystorage.com/layers/523ef1d23f222195488575f52a39c729c76a8c5630c9a194139cb246fb212da6/layer.tar",
"Headers": {
"Authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiY
WRtaW4iOnRydWV9.EkN-DOsnsuRjRO6BxXemmJDm3HbxrbRzXglbN2S4sOkopdU4IsDxTI8jO19W_A4K8ZPJijNLis4EZsHeY559a4DFOd50_OqgHGu
ERTqYZyuhtF39yxJPAjUESwxk2J5k_4zM3O-vtd1Ghyo4IbqKKSy6J9mTniYJPenn5-HIirE"
},
"ParentName": "140f9bdfeb9784cf8730e9dab5dd12fbd704151cf555ac8cae650451794e5ac2",
"Format": "Docker"
}
}
该步操作是将指定镜像的每一层按照顺序push到clair中。
官网说明
为了分析容器图像,必须按照正确的顺序push某个镜像的所有层。
例如,要分析由三层A-> B-> C组成的图像,其中A是基础层,
必须对此路径进行三次API调用,从A到C.此外,在分析B时,
必须将A设置为父级,然后在分析C时,必须将B定义为父级。
Name为层id,或为一个不与其他图层id重复的字符串
Path位图层的下载地址
Authorization字段是一个可选值,可选,其内容将在通过HTTP
请求图层时填充Authorization HTTP Header。
ParentName为父级id
Format固定为Docker
因此目前我们的思路为打包测试用的image,再将解压后的文件复制到tomcat的ROOT目录中,在post的过程中注意层级的父子关系。
1.将某个镜像打包
docker save -o /root/data/nginx.tar nginx:latest
2.解压该镜像
tar -xvf nginx.tar
3.查看镜像的层级关系
vi manifest.json
输出
[{
"Config": "e445ab08b2be8b178655b714f89e5db9504f67defd5c7408a00bade679a50d44.json",
"RepoTags": ["nginx:latest"],
"Layers": [
"08029cfa8b0dcfe678e23255feef9c9f1a08c39ebf6faffc4e5ee8b6ec63ff1f/layer.tar",
"656e37151781143bab1be21edfbe2de2251ffe28e8d2004e83cd79a7a76d0b81/layer.tar",
"22e7e6fdc1e671b8f4d55efa2e707cf4ac35e34d757efc2b43c219be7a800ae4/layer.tar"
]
}]
由此可知,08029为最底层,656e为中间层,22e7为最上层。
4.复制文件到tomcat的ROOT目录中
cp -r /root/data/* /usr/local/apache-tomcat-8.5.42/webapps/ROOT
5.发起请求
5.1 push最底层
请求示例
post
http://192.168.2.186:6060/v1/layers
{
"Layer": {
"Name": "08029cfa8b0dcfe678e23255feef9c9f1a08c39ebf6faffc4e5ee8b6ec63ff1f",
"Path": "http://localhost:8080/08029cfa8b0dcfe678e23255feef9c9f1a08c39ebf6faffc4e5ee8b6ec63ff1f/layer.tar",
"Format": "Docker"
}
}
5.2 push中间层
请求示例
post
http://192.168.2.186:6060/v1/layers
{
"Layer": {
"Name": "656e37151781143bab1be21edfbe2de2251ffe28e8d2004e83cd79a7a76d0b81",
"Path": "http://localhost:8080/656e37151781143bab1be21edfbe2de2251ffe28e8d2004e83cd79a7a76d0b81/layer.tar",
"ParentName": "08029cfa8b0dcfe678e23255feef9c9f1a08c39ebf6faffc4e5ee8b6ec63ff1f",
"Format": "Docker"
}
}
5.3 push最上层
请求示例
post
http://192.168.2.186:6060/v1/layers
{
"Layer": {
"Name": "22e7e6fdc1e671b8f4d55efa2e707cf4ac35e34d757efc2b43c219be7a800ae4",
"Path": "http://localhost:8080/22e7e6fdc1e671b8f4d55efa2e707cf4ac35e34d757efc2b43c219be7a800ae4/layer.tar",
"ParentName": "656e37151781143bab1be21edfbe2de2251ffe28e8d2004e83cd79a7a76d0b81",
"Format": "Docker"
}
}
6.获取镜像漏洞
请求示例
get
http://192.168.2.186:6060/v1/layers/22e7e6fdc1e671b8f4d55efa2e707cf4ac35e34d757efc2b43c219be7a800ae4?features&vulnerabilities
响应示例
{
"Layer": {
"Name": "22e7e6fdc1e671b8f4d55efa2e707cf4ac35e34d757efc2b43c219be7a800ae4",
"NamespaceName": "debian:10",
"ParentName": "656e37151781143bab1be21edfbe2de2251ffe28e8d2004e83cd79a7a76d0b81",
"IndexedByVersion": 3,
"Features": [
{
"Name": "libgd2",
"NamespaceName": "debian:10",
"VersionFormat": "dpkg",
"Version": "2.2.5-5.2",
"AddedBy": "656e37151781143bab1be21edfbe2de2251ffe28e8d2004e83cd79a7a76d0b81"
},
{
"Name": "openssl",
"NamespaceName": "debian:10",
"VersionFormat": "dpkg",
"Version": "1.1.1c-1",
"AddedBy": "656e37151781143bab1be21edfbe2de2251ffe28e8d2004e83cd79a7a76d0b81"
},
{
"Name": "util-linux",
"NamespaceName": "debian:10",
"VersionFormat": "dpkg",
"Version": "2.33.1-0.1",
"AddedBy": "08029cfa8b0dcfe678e23255feef9c9f1a08c39ebf6faffc4e5ee8b6ec63ff1f"
},
...........
]
}
}
备注:这种方式和第一种方式,在分析nginx时,得到了相同的分析结果(未发现任何漏洞)
2019/0905再次调研结果:
无任何本质性改变,此外clairctl在两年前就已经停更,存在的问题很多
安装中出现的问题
1.clair官方镜像***很慢,配置阿里云镜像加速依然无效,多次出现下载失败的情形。
2.clairctl官方github提供的安装命令中出现的部分链接已经失效。
3.clair更新漏洞元数据频繁出现超时、文件解析等错误。在多次更换版本的情况下(尝试了2.0.1与最新版v2.0.9),依然未解决。
在漏洞元数据未更新完全的情况下,使用分析命令后得不到正确的结果。
总结
一、之前的扫描镜像原理
之前的镜像扫描采用的是clair的1.0版本,首先前端发起扫描请求,
后端接收到请求后再调用abcsys服务,abcsys调用clairctl,clairctl在内部调用clair的api,
可能由于clair的版本太低,出现了很多镜像不支持的问题。
二、使用中出现的问题
(1)漏洞元下载缓慢,经查阅信息,漏洞元数据需要3个小时左右才可以下载完毕。
(2)扫描任何镜像都查不出漏洞,经过排查,发现特征-漏洞关系表下载不下来,
导致clair仅能分析出特征,而不能扫描出漏洞,目前该问题无法解决。
下载不下来的问题,可能由于该地址已经失效,或者需要***才可以访问。
三、两个可选的clair扫描策略
(1)继续使用clairctl+clair的扫描方式,但clairctl停更已久,可能会出现意想不到的问题。
(2)直接使用clair api的方式,只不过这个需要分层提交。这样避免使用clairctl,
在漏洞元数据填充完毕后,调用方将获得扫描的json字符串,解析简单方便。但这样的缺点是
需要分层提交。
参考文档
1.clair官网
2.clair官方Github
3.clair官方api
4.clairctl官方github
5.安全防护工具之:Clair
6.安全工具:无疾而终的clairctl
安装出现问题的解决记录
[1]open file:permission denied
尽管给文件设置777权限后,依然被拒绝
解决方案:https://github.com/coreos/clair/issues/707
执行命令setenforce 0 ,注意这只是临时解决方式,一旦机器重启,则依然出现权限拒绝的问题,
因此需要永久性解决,永久解决方案https://www.cnblogs.com/ftl1012/p/selinux.html(该方案未尝试)
[2]postgres数据库出现大量的同步错误
解决方案:可以下载210上的postgres中的public库,目前该库是harbor集成clair所依赖的数据库,该数据库中存储的漏洞比较全,
且表结构与v2.0.1的clair所依赖的数据库一致,可以直接导入到postgres中。
但依然会在导入的过程中出现问题,即使删除postgres中的库,直接复制库也不行。
在复制库的过程中,会出现主键丢失与数据丢失的问题,造成的结果就是clair分析出的漏洞不全。
该问题暂时未解决,估摸着是外键依赖的问题。
[3]clairctl无法向clair push镜像
clairctl虽然能与clair正常连接,即clairctl能对clair进行健康检查。但clairctl无法向clair push镜像,
会收到clair 400的错误。目前该问题未解决。
可能的原因:clairctl的版本和clair的版本不对应,造成clairctl的请求和clair的api对不上的问题。
最终可用版
数据库方面的考虑
为了避免clair依赖的postgres数据同步失败或不完整的情况,以及为了好harbor的扫描结果一致,因此打算直接使用harbor的扫描漏洞库。
因此,需要需要添加配置文件config.yml
在/root/data/clair/config下添加config.yml
database:
type: pgsql
options:
source: postgresql://postgres:password@192.168.2.194:5432/postgres?sslmode=disable
cachesize: 16384
api:
port: 6060
healthport: 6061
timeout: 900s
updater:
interval: 2h
notifier:
attempts: 3
renotifyinterval: 2h
启动clair
运行命令
docker run --net=host -d -p 6060-6061:6060-6061 -v /root/data/clair/config:/config quay.io/coreos/clair:v2.0.1 -config=/config/config.yml
请求clair的api
(1)将镜像打包,并解压至tomcat的ROOT目录中,这一步主要是提供一个模拟的服务器,用于提供待
扫描镜像的每一层的下载地址。
(2)获取ROOT目录中的manifest.json,此文件是描述该镜像的层序结构。
{
"Config": "158ad321fec9a1621f3251d68f91d53f1003c93cca29ded31d00bd1ade2c3a47.json",
"RepoTags": [
"192.168.2.194/public/fabric-node:0.0.6rc17"
],
"Layers": [
"c5da0099e17668eceecd5f30e5b2012089d9def80a64be8a9e6adc85197528a7/layer.tar",
"dc875156727c2cd7c2f8c85aaa2fe5f33a4ff699edd52ab2b021f7abc74a16c6/layer.tar",
"48a07fbdca8996ebce7ff7ad95c793a57b1676e2b05ec32bb2bfde32025e90ed/layer.tar",
"bd85a4f03f646be6fc9393230f98053bc44de3346ffa96da51f1307eb1f43ee4/layer.tar",
"cc4a551d9f992fc502d601340febca9ba5eecbbf006e3cfb38269fb4f29960cb/layer.tar",
"c90086e0cafb06746235f5139b9e5dbfdfe29ad4478eed876be2766cbc097222/layer.tar",
"3997751112581455a0b622c5b6ce74cffabd0be2507b345450caa505a358f7f2/layer.tar"
]
}
(3)循环layers,依次请求clair的api,以第二层为例
POST http://192.168.2.186:6060/v1/layers
{
"Layer": {
"Name": "dc875156727c2cd7c2f8c85aaa2fe5f33a4ff699edd52ab2b021f7abc74a16c6",
"Path": "http://localhost:8080/dc875156727c2cd7c2f8c85aaa2fe5f33a4ff699edd52ab2b021f7abc74a16c6/layer.tar",
"ParentName": "c5da0099e17668eceecd5f30e5b2012089d9def80a64be8a9e6adc85197528a7",
"Format": "Docker"
}
}
其中Name为镜像该层的名称;Path为该层的下载地址;Parent为该层的父层名称,当该层已经是第一层是,则不传该参数;Format格式固定为Docker
获取分析结果
其中镜像名称为最后一层的名称,数据库会按照当初存入的层级关系,一直找到该层链接的所有层,并将所有层的特征-漏洞二元组返回。
GET http://192.168.2.186:6060/v1/layers/3997751112581455a0b622c5b6ce74cffabd0be2507b345450caa505a358f7f2?features&vulnerabilities
返回结果示例:
{
"Layer":{
"Name":"3997751112581455a0b622c5b6ce74cffabd0be2507b345450caa505a358f7f2",
"NamespaceName":"centos:7",
"ParentName":"c90086e0cafb06746235f5139b9e5dbfdfe29ad4478eed876be2766cbc097222",
"IndexedByVersion":3,
"Features":Array[145]
}
}