镜像扫描结构图

镜像扫描结构图

方式2的具体操作步骤

api扫描方式的具体步骤

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]
    }
}
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐