I’m gonna tell you how you can add a step in your CI pipeline to check if the Docker image you’re build contains vulnerabilities or not.
Pre-requisites
I assume you’ve Docker installed on your system. We’re gonna use Jenkins but is optional.
Clair
Clair is an open source project for the static analysis of vulnerabilities in appc and docker containers. It’s been developed by CoreOS.
Vulnerabilities Database
Clair relay on Postgres to keep the database. We’ve two different options here. We can create a new Postgres database which will be initializes when Clair starts or we can use a database already provisioned.
If you want to try the first option keep in mind it takes a while to feed the database (10~15 minutes depends on network).
We’re using the second option here. Go to a terminal and run this command:
$ docker network create ci
$ docker volume create --name clair-postgres
$ docker run --detach \
--name clair-postgres \
--publish 5432:5432 \
--net ci \
--volume clair-postgres:/var/lib/postgresql/data \
arminc/clair-db:latest
We just have created a new network to Clair can resolve the database by its name, a volume and a Docker container. After a couple seconds we can check the database is up and ready.
$ docker logs --tail 1 clair-postgres
2019-05-15 13:36:00.068 UTC [1] LOG: database system is ready to accept connections
Clair Service
Now it’s time to run the service. But first, a little configuration is needed. We must set the database for Clair in the config file. Run the following command:
$ curl --silent https://raw.githubusercontent.com/nordri/config-files/master/clair/config-clair.yaml | sed "s/POSTGRES_NAME/clair-postgres/" > config.yaml
We’re changing the string POSTGRES_NAME by clair-postgres which is the name we’ve gave to the Postgres container. Now, we can launch the Clair container by running:
$ docker run --detach \
--name clair \
--net ci \
--publish 6060:6060 \
--publish 6061:6061 \
--volume ${PWD}/config.yaml:/config/config.yaml \
quay.io/coreos/clair:latest -config /config/config.yaml
And that is.
Launch an scanner
It’s time to check everything is working properly. To run the Clair scanner I’ve just built a container with the latest release. You can found it here and then build the image using this Dockerfile.
FROM debian:jessie
COPY clair-scanner_linux_amd64 /clair-scanner
RUN chmod +x /clair-scanner
I already did so you can use this image: nordri/clair-scanner
.
Let’s check some images now. One of Clair limitations is it cannot scan remote images. All images must be locals. To scan an image just launch
$ docker run -ti \
--rm \
--name clair-scanner \
--net ci \
-v /var/run/docker.sock:/var/run/docker.sock \
nordri/clair-scanner:latest /bin/bash
Now we’re inside the container and we’re able to launch the scanner:
# export IP=$(ip r | tail -n1 | awk '{ print $9 }')
# /clair-scanner --ip ${IP} --clair=http://clair:6060 debian:jessie
If we launch that as it, we’ll see an endless list of vulnerabilities, which is more noise than something useful. So, we can filter by severity using this flag:
-t, --threshold="Unknown" CVE severity threshold. Valid values; 'Defcon1', 'Critical', 'High', 'Medium', 'Low', 'Negligible', 'Unknown'
We can choose only check for critical or higher and then the list will be much more gentle because all the vulnerabilities will be treat as approved, and, which is much more interested, the command will exit with 0. Then we can add this to a CI pipeline.
Jenkins Integration
If you’ve Jenkins running in your infrastructure, you’ll be probably interested in check the images you’re delivering to your customers.
This simple Jenkinsfile can solve the problem
node {
docker.image('docker').inside('-v /var/run/docker.sock:/var/run/docker.sock') {
stage ('Checkout') {
checkout scm
}
stage ('Build Docker image') {
// Build docker image
// docker build... DOCKER_IMAGE
}
}
docker.image('nordri/clair-scanner').inside('--net ci') {
stage ('Security scanner') {
sh '''
IP=$(ip r | tail -n1 | awk '{ print $9 }')
/clair-scanner --ip ${IP} --clair=http://clair:6060 --threshold="Critical" DOCKER_IMAGE
'''
}
}
}
As we can see, we’re using a container to build the image and another one to scan that image for vulnerabilities, we set the threshold to Critical so only really big problem will made the pipeline to fail.