Hey guys, so i wanna share with you one of the toughest task I’ve had to face so far in my DevOps journey. So my boss gave me a task to scan a container in my gitlab ci. I was yet again excited to go try do something i had never delved into and then i began my research. I spent the next week on this singular task and i kept having several errors, I checked several github repos, blogs and what have you but i kept having one error after the other. I did this into the second week, it was my toughest task ever, and then the next week, i got a put through on my yml script. So after about 2 weeks, i finally found a breakthrough and i’ll just drop the process i took to achieve success on the task.

The first thing i did was to create a custom runner for the scan job, you can check out my post on registering a scanner if you have difficulties doing that here :- https://medium.com/@stevedot.olabode/creating-a-custom-runner-on-gitlab-800fec724d6e . The next thing i did was to edit my yml script, first by stating the variables and the stage. At this juncture, i’ll go through each line as strictly as possible…..

variables:
DOCKER_TLS_CERTDIR: "/certs"

stages:
- scan

scan:
image: docker:latest
stage: scan

This is the variable stated as well as the stage to which our project is limited to. Seeing that we have just one stage, scan, the basic ‘image and stage’ sub stages are set to default values. The next step then is to run the ‘before scripts’ i.e. tools or containers we need to have in place so we can have a smooth run of the main task. This contains a series of little commands to create some services and containers so we can have a basis to run the scanner upon.

before_script:
- apk update && apk add coreutils
- docker info
- docker pull nginx:latest
- docker network create scanning
- docker run -p 5432:5432 -d --net=scanning --name db arminc/clair-db:$(date -d "today" '+%Y-%m-%d') ; sleep 10
- docker run -p 6060:6060 --net=scanning --link db:postgres -d --name clair arminc/clair-local-scan:v2.0.1 ; sleep 10

I’ll like to explain each line so we can have an understanding of why they’re needed. The first line, ‘apk update && apk add coreutils’ is as though we’re working on our local terminal and there’s need to update the dependencies. Working on the CI, we also have to install and update the dependencies so the tools can work fine as expected. The next line, ‘docker info’ as it suggests gives a instruction to give basic information on the docker running on the machine already. Then on the next line, we have ‘docker pull nginx:latest’ which basically pulls an nginx file from docker hub. Now this file we are pulling is the file we are gonna scan using the clair scanner, however, if you have an image or a file already on your machine that you wanna scan, there’s absolutely no need for this line. Moving forward, the next line to be analyzed is the ‘docker network create scanning’. Now, when working with clair, there’s need to create a network which will serve as a basis for the clair server to operate on, hence the need to create this network. This is the network upon which clair will be installed and will connect with the file on your host machine. The next line is the ‘- docker run -p 5432:5432 -d — net=scanning — name db arminc/clair-db:$(date -d “today” ‘+%Y-%m-%d’) ; sleep 10’. This is a docker container we’re trying to create here. The ‘-p 5432:5432’ is a port that we’re defining for this container named db. These are the ports that links the clair server to the host machine i.e. assigning the port 5432 on your local machine to port 5432 on the clair server network we just created. The ‘arminc/clair-db’ also is the custom docker image from docker hub upon which this container is built. As you might already know, to build a docker container, there has to be a docker image upon which the container is to be built so here, we’re building our container, db, upon the custom arminc clair image from the docker hub. The final line ‘docker run -p 6060:6060 — net=scanning — link db:postgres -d — name clair arminc/clair-local-scan:v2.0.1 ; sleep 10’ just as the previously explained, we’re equally trying to create another container, clair, on a custom docker image arminc/clair-local-scan with version 2.0.1 .

The next step now is the main script(job) to be conjured, which is the scanning of the nginx file that was earlier pulled from the docker repo. This is achieved using this command

- docker run --net=scanning --rm --name=scanner --link=clair:clair  -v '/var/run/docker.sock:/var/run/docker.sock' objectiflibre/clair-scanner --clair="http://clair:6060" --ip="scanner" -t Medium nginx:latest

This command is as basic and simple as typed, however, i’ll go ahead and explain each command in simple terms. We specified the network with the ‘ — net’ command, the name, the link i.e ‘clair:clair’ which is to explain the link between our host system and the clair server. We also specified the volume to which we’re building our container upon with the ‘- v’ command, the ‘objectiflibre/clair-scanner’ which is the dockerized clair scanner and then the ‘ — clair’ which specifies the custom clair server that we’re connecting to. In addition, we did the ‘ — ip’ that specifies the ip address and finally the nginx:latest which is the actual image to be scanned.

Finally, there’s an after script which removes the containers created on the before script, basically to provide a fresh start in a case where we’ll need to run the whole process again

after_script:
- docker rm -vf db clair
- docker network rm scanning

At the end, our final .gitlab-ci.yml looks more like this and with your runner properly configured and registered, you should have a succeeded job at the end of your build as shown below.

variables:
DOCKER_TLS_CERTDIR: "/certs"

stages:
- scan

scan:
image: docker:latest
stage: scan
before_script:
- apk update && apk add coreutils
- docker info
- docker pull nginx:latest
- docker network create scanning
- docker run -p 5432:5432 -d --net=scanning --name db arminc/clair-db:$(date -d "today" '+%Y-%m-%d') ; sleep 10
- docker run -p 6060:6060 --net=scanning --link db:postgres -d --name clair arminc/clair-local-scan:v2.0.1 ; sleep 10
script:
- docker run --net=scanning --rm --name=scanner --link=clair:clair -v '/var/run/docker.sock:/var/run/docker.sock' objectiflibre/clair-scanner --clair="http://clair:6060" --ip="scanner" -t Medium nginx:latest
after_script:
- docker rm -vf db clair
- docker network rm scanning

So there you have it guys, how to have a successful scan of your containers for vulnerabilities using clair scanner. Thanks for reading, I’m available for any questions regarding this post.

Cheers.

DevOps Engineer | AWS Enthusiast | Cloud Computing | Solutions Architect