Scan Docker Images for Security Vulnerabilities using Clair

In this article, we will do vulnerability scanning of Docker container images using Clair.

In CentOS7 VM (centos7-test-01), I am creating work-space directory to store Clair related configuration files as below,

[root@centos7-test-01 ~]# cat /etc/redhat-release
CentOS Linux release 7.7.1908 (Core)
[root@centos7-test-01 ~]# pwd
/root
[root@centos7-test-01 ~]# mkdir CLAIR_SCAN
[root@centos7-test-01 ~]# cd CLAIR_SCAN/
[root@centos7-test-01 CLAIR_SCAN]#

Download the sample clair config file as below using curl,

curl -L https://raw.githubusercontent.com/coreos/clair/master/config.yaml.sample -o config.yaml


[root@centos7-test-01 CLAIR_SCAN]# curl -L https://raw.githubusercontent.com/coreos/clair/master/config.yaml.sample -o config.yaml
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 3108 100 3108 0 0 4481 0 --:--:-- --:--:-- --:--:-- 4491
[root@centos7-test-01 CLAIR_SCAN]# ls -lrt
total 4
-rw-r--r--. 1 root root 3108 Apr 8 05:24 config.yaml
[root@centos7-test-01 CLAIR_SCAN]#

Now we will create ‘postgres’ DB container to store vulnerability info database

docker run –name postgres_db -d -e POSTGRES_PASSWORD=”password” -p 5432:5432 postgres:9.6

Container name: postgres_db

[root@centos7-test-01 CLAIR_SCAN]# docker run --name postgres_db -d -e POSTGRES_PASSWORD="password" -p 5432:5432 postgres:9.6
691f82e700d27b76ce7ef8c9a8cbc01f50c5f498981bffeec1882dbc277c1117
[root@centos7-test-01 CLAIR_SCAN]#

‘postgres_db’ container is up and running as below,

[root@centos7-test-01 CLAIR_SCAN]# docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
691f82e700d2 postgres:9.6 "docker-entrypoint.s…" 15 seconds ago Up 13 seconds 0.0.0.0:5432->5432/tcp postgres_db
[root@centos7-test-01 CLAIR_SCAN]#

we have to modify the Clair config file with our DB details

[root@centos7-test-01 CLAIR_SCAN]# cat config.yaml | grep -i source
source: host=localhost port=5432 user=postgres sslmode=disable statement_timeout=60000
# Frequency the database will be updated with vulnerabilities from the default data sources
[root@centos7-test-01 CLAIR_SCAN]#

Update above source entry as below,
source: host=postgres_db password=password port=5432 user=postgres sslmode=disable statement_timeout=60000

After modification:

[root@centos7-test-01 CLAIR_SCAN]# cat config.yaml | grep -i source
source: host=postgres_db password=password port=5432 user=postgres sslmode=disable statement_timeout=60000
# Frequency the database will be updated with vulnerabilities from the default data sources
[root@centos7-test-01 CLAIR_SCAN]#

Now we create Clair container as below,

docker run –name clair_vulscanner –link postgres_db -d -p 6060-6061:6060-6061 -v /root/CLAIR_SCAN:/config quay.io/coreos/clair:latest -config=/config/config.yaml

[root@centos7-test-01 CLAIR_SCAN]# docker run --name clair_vulscanner --link postgres_db -d -p 6060-6061:6060-6061 -v /root/CLAIR_SCAN:/config quay.io/coreos/clair:latest -config=/config/config.yaml
341e2eea8c15e8a05bbb6b16c6b559a08a8fc2e1c9158032f568efc1cf2dad7b
[root@centos7-test-01 CLAIR_SCAN]#

[root@centos7-test-01 CLAIR_SCAN]# docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
341e2eea8c15 quay.io/coreos/clair:latest "/clair -config=/con…" 21 seconds ago Up 19 seconds 0.0.0.0:6060-6061->6060-6061/tcp clair_vulscanner
691f82e700d2 postgres:9.6 "docker-entrypoint.s…" 8 minutes ago Up 8 minutes 0.0.0.0:5432->5432/tcp postgres_db
[root@centos7-test-01 CLAIR_SCAN]#

We can check ‘clair_vulscanner’ logs as below and it shows that it fetching the ‘vulnerability updates’ and storing it in our ‘postgres_db’ container.

[root@centos7-test-01 CLAIR_SCAN]# docker logs -f clair_vulscanner
{"Event":"running database migrations","Level":"info","Location":"pgsql.go:216","Time":"2020-04-08 05:37:02.328486"}
{"Event":"database migration ran successfully","Level":"info","Location":"pgsql.go:223","Time":"2020-04-08 05:37:02.479245"}
{"Event":"starting health API","Level":"info","Location":"api.go:85","Time":"2020-04-08 05:37:02.479691","port":6061}
{"Event":"notifier service is disabled","Level":"info","Location":"notifier.go:77","Time":"2020-04-08 05:37:02.479827"}
{"Event":"updater service started","Level":"info","Location":"updater.go:83","Time":"2020-04-08 05:37:02.479898","lock identifier":"625c4c7c-612d-4ed9-b287-6916517b139a"}
{"Event":"starting main API","Level":"info","Location":"api.go:52","Time":"2020-04-08 05:37:02.480022","port":6060}
{"Event":"updating vulnerabilities","Level":"info","Location":"updater.go:192","Time":"2020-04-08 05:37:02.487169"}
{"Event":"fetching vulnerability updates","Level":"info","Location":"updater.go:239","Time":"2020-04-08 05:37:02.487224"}
{"Event":"Start fetching vulnerabilities","Level":"info","Location":"alpine.go:52","Time":"2020-04-08 05:37:02.487351","package":"Alpine"}
{"Event":"Start fetching vulnerabilities","Level":"info","Location":"oracle.go:119","Time":"2020-04-08 05:37:02.487621","package":"Oracle Linux"}
{"Event":"Start fetching vulnerabilities","Level":"info","Location":"debian.go:63","Time":"2020-04-08 05:37:02.487780","package":"Debian"}
{"Event":"Start fetching vulnerabilities","Level":"info","Location":"rhel.go:92","Time":"2020-04-08 05:37:02.488915","package":"RHEL"}
{"Event":"Start fetching vulnerabilities","Level":"info","Location":"ubuntu.go:85","Time":"2020-04-08 05:37:02.491411","package":"Ubuntu"}
{"Event":"finished fetching","Level":"info","Location":"updater.go:253","Time":"2020-04-08 05:37:05.057780","updater name":"alpine"}
{"Event":"Debian bullseye is not mapped to any version number (eg. Jessie-\u003e8). Please update me.","Level":"warning","Location":"debian.go:134","Time":"2020-04-08 05:37:08.860001"}
{"Event":"finished fetching","Level":"info","Location":"updater.go:253","Time":"2020-04-08 05:37:08.860089","updater name":"debian"}

Our Clair environment set up is completed, now we can proceed to scan docker images.

For this, I will use ‘klar’ to get integration of Clair and Docker Registry.

Download the Klar from below URL,

wget https://github.com/optiopay/klar/releases/download/v2.4.0/klar-2.4.0-linux-amd64

[root@centos7-test-01 CLAIR_SCAN]# wget https://github.com/optiopay/klar/releases/download/v2.4.0/klar-2.4.0-linux-amd64
--2020-04-08 05:43:40-- https://github.com/optiopay/klar/releases/download/v2.4.0/klar-2.4.0-linux-amd64
Resolving github.com (github.com)... 13.234.176.102
Connecting to github.com (github.com)|13.234.176.102|:443... connected.
HTTP request sent, awaiting response... 302 Found
....
Connecting to github-production-release-asset-2e65be.s3.amazonaws.com (github-production-release-asset-2e65be.s3.amazonaws.com)|52.216.200.11|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 12727985 (12M) [application/octet-stream]
Saving to: ‘klar-2.4.0-linux-amd64’

100%[===================================================================================================================================================>] 12,727,985 1.13MB/s in 18s

2020-04-08 05:44:00 (701 KB/s) - ‘klar-2.4.0-linux-amd64’ saved [12727985/12727985]

[root@centos7-test-01 CLAIR_SCAN]#

[root@centos7-test-01 CLAIR_SCAN]# ls
config.yaml klar-2.4.0-linux-amd64
[root@centos7-test-01 CLAIR_SCAN]# mv klar-2.4.0-linux-amd64 klar
[root@centos7-test-01 CLAIR_SCAN]# mv klar /usr/local/bin/
[root@centos7-test-01 CLAIR_SCAN]# chmod +x /usr/local/bin/klar
[root@centos7-test-01 CLAIR_SCAN]#

To scan and detect the vulnerabilities, I am taking some random and old image named ‘drupaldocker/apache’ from Docker Hub.

proceeding the scan with below command

CLAIR_ADDR=http://localhost:6060 CLAIR_OUTPUT=Low CLAIR_THRESHOLD=10 klar drupaldocker/apache

[root@centos7-test-01 ~]# CLAIR_ADDR=http://localhost:6060 CLAIR_OUTPUT=Low CLAIR_THRESHOLD=10 klar drupaldocker/apache
clair timeout 1m0s
docker timeout: 1m0s
no whitelist file
Analysing 11 layers
Got results from Clair API v1
Found 536 vulnerabilities
Unknown: 387
Negligible: 119
Low: 30

CVE-2018-7169: [Low]
Found in: shadow [1:4.2-3+deb8u4]
Fixed By:
An issue was discovered in shadow 4.5. newgidmap (in shadow-utils) is setuid and allows an unprivileged user to be placed in a user namespace where setgroups(2) is permitted. This allows an attacker to remove themselves from a supplementary group, which may allow access to certain filesystem paths if the administrator has used "group blacklisting" (e.g., chmod g-rwx) to restrict access to paths. This flaw effectively reverts a security feature in the kernel (in particular, the /proc/self/setgroups knob) to prevent this sort of privilege escalation.
https://security-tracker.debian.org/tracker/CVE-2018-7169
-----------------------------------------
CVE-2016-3189: [Low]
Found in: bzip2 [1.0.6-7]
Fixed By: 1.0.6-7+deb8u1
Use-after-free vulnerability in bzip2recover in bzip2 1.0.6 allows remote attackers to cause a denial of service (crash) via a crafted bzip2 file, related to block ends set to before the start of the block.
https://security-tracker.debian.org/tracker/CVE-2016-3189
-----------------------------------------

Hence we have scanned an image and identified vulnerabilities as below,

Got results from Clair API v1
Found 536 vulnerabilities
Unknown: 387
Negligible: 119
Low: 30

#######
Reference:
https://github.com/coreos/clair
https://github.com/optiopay/klar