When using Docker containers in production, we need to ensure that we are following best practices. In this post, we will focus on Ensure images are scanned and rebuilt to include security patches from the CIS Docker Community Benchmark which we discussed previously. The item states that you should scan your images “frequently” for any vulnerabilities and then take the necessary actions to mitigate these vulnerabilities. We will use Anchore Engine in order to accomplish this.

1. What Is Anchore Engine?

Anchore Engine is an open source tool that scans your Docker images for security vulnerabilities. Definitely a powerful tool when you are running Docker containers in production. With Anchore Engine you have an automatic check on your Docker images whether they are up to date before you run them into production. In this post we will focus on Anchore Engine, which is the open source project. Anchore also exists as an Enterprise version which provides you an easier way of configuring Anchore by means of a UI, role based access control, generate reports, etc. A comparison of features between Anchore Engine and Anchore Enterprise can be found here (near the end of the page).

Anchore Engine can be accessed by means of a RESTful API or by means of the Anchore CLI. We will use the latter in this post.

2. Install Anchore Engine

The Anchore Engine can easily be installed by following the installation instructions. A Docker image is available with Anchore Engine and besides that, a PostgreSQL database is needed. It becomes even easier because a Docker Compose yaml file is provided. For more information on Docker Compose, we suggest reading a previous post about this topic.

So, let’s get started. First, we create a configuration directory into our home directory of, in our case, user developer (this is different from the installation instructions where the root path is used).

$ mkdir -p /home/developer/anchorevolume/config

Go to the just created directory /home/developer/anchorevolume/config and download the config.yaml file:

$ curl -O https://raw.githubusercontent.com/anchore/anchore-engine/master/scripts/docker-compose/config.yaml

We will not change anything to the default configuration but you should take a closer look at it in order to tweak it to your preferences. In our case, the default admin user admin with password foobar will be used.

We also need a directory to store the data for our PostgreSQL database:

$ mkdir -p /home/developer/anchorevolume/db

As mentioned before, we will run Anchore Engine by means of Docker Compose. Go to the directory /home/developer/anchorevolume and download the docker-compose.yaml file:

$ curl -O https://raw.githubusercontent.com/anchore/anchore-engine/master/scripts/docker-compose/docker-compose.yaml

From within the same directory, run the following command which will pull the Docker images for Anchore Engine and PostgreSQL:

$ docker-compose pull

The only thing left to do, is to run Anchore Engine in silent mode:

$ docker-compose up -d

3. Install Anchore CLI

We will use Anchore CLI in order to control the Anchore Engine. Installation of Anchore CLI is also fairly simple. We are using Ubuntu 18.04, installation instructions for other platforms are available here.

$ apt-get update
$ apt-get install python-pip
$ pip install anchorecli

We also had to source our .profile file in order to get Anchore CLI working:

$ source ~/.profile

Correct installation can be verified by issuing the following command:

$ anchore-cli --version

With every other command, you will have to provide the Anchore Engine credentials by means of the parameters --u for the user, --p for the password and --url for accessing the Anchore Engine API. Another way is to define these parameters as environment variables. This way, you will not have to provide them with every command. In our case, this will be:

$ ANCHORE_CLI_URL=http://localhost:8228/v1

4. Analyze a Docker Image

Now that the installation of Anchore Engine and Anchore CLI is all set and done, we are ready to analyze a Docker image. We will make use of the openjdk:10-jdk Docker image because we are quite certain that it will contain vulnerabilities. This Docker image will not be updated anymore because it was not a Java LTS release and is being replaced by the openjdk:11-jdk Docker image.

The only thing we need to do, is to add the Docker image to Anchore Engine, which will immediately start the analysis.

$ anchore-cli image add openjdk:10-jdk
Image Digest: sha256:923d074ef1f4f0dceef68d9bad8be19c918d9ca8180a26b037e00576f24c2cb4
Parent Digest: sha256:9f17c917630d5e95667840029487b6561b752f1be6a3c4a90c4716907c1aad65
Analysis Status: not_analyzed
Image Type: docker
Image ID: b11e88dd885d8b2621d448f3d2099068d181c5c778c2ab0cf0f61b573fa429b7
Dockerfile Mode: None
Distro: None
Distro Version: None
Size: None
Architecture: None
Layer Count: None

Full Tag: docker.io/openjdk:10-jdk

We are able to monitor the progress with the list command, which shows us the Docker images added to Anchore Engine and their respective status.

$ anchore-cli image list
Full Tag                    Image ID                                                            Analysis Status        
docker.io/openjdk:10-jdk    b11e88dd885d8b2621d448f3d2099068d181c5c778c2ab0cf0f61b573fa429b7    analyzing

After a few minutes, the Analysis Status turns into Analyzed. We are now able to retrieve the results. It is possible that you will need to wait a little bit longer than a few minutes. The first time Anchore Engine is run, it needs to download the vulnerabilities. This can take some time dependent of your internet bandwidth. You can check the status of the sync with the following command:

$ anchore-cli system feeds list
Feed                   Group                  LastSync                          RecordCount        
vulnerabilities        alpine:3.3             2019-05-19T11:45:22.273938        457                
vulnerabilities        alpine:3.4             2019-05-19T11:45:28.743787        681                
vulnerabilities        alpine:3.5             2019-05-19T11:45:36.883590        875                
vulnerabilities        alpine:3.6             2019-05-19T11:45:47.375153        1051               
vulnerabilities        alpine:3.7             2019-05-19T11:45:58.165606        1125               
vulnerabilities        alpine:3.8             2019-05-19T11:46:09.939298        1220               
vulnerabilities        alpine:3.9             2019-05-19T11:46:22.068746        1218               
vulnerabilities        amzn:2                 2019-05-19T11:46:29.720936        167                
vulnerabilities        centos:5               2019-05-19T11:46:57.667760        1323               
vulnerabilities        centos:6               2019-05-19T11:47:37.387118        1331               
vulnerabilities        centos:7               2019-05-19T11:48:04.611414        788                
vulnerabilities        debian:10              2019-05-19T11:51:09.159110        20196              
vulnerabilities        debian:7               2019-05-19T11:54:29.383399        20455              
vulnerabilities        debian:8               2019-05-19T11:57:48.854096        21641              
vulnerabilities        debian:9               2019-05-19T12:00:47.974081        20412              
vulnerabilities        debian:unstable        2019-05-19T12:03:57.095763        21058              
vulnerabilities        ol:5                   2019-05-19T12:04:30.601427        1232               
vulnerabilities        ol:6                   2019-05-19T12:05:08.700762        1410               
vulnerabilities        ol:7                   2019-05-19T12:05:39.294882        906                
vulnerabilities        ubuntu:12.04           None                              0                  
vulnerabilities        ubuntu:12.10           None                              0                  
vulnerabilities        ubuntu:13.04           None                              0                  
vulnerabilities        ubuntu:14.04           None                              0                  
vulnerabilities        ubuntu:14.10           None                              0                  
vulnerabilities        ubuntu:15.04           None                              0                  
vulnerabilities        ubuntu:15.10           None                              0                  
vulnerabilities        ubuntu:16.04           None                              0                  
vulnerabilities        ubuntu:16.10           None                              0                  
vulnerabilities        ubuntu:17.04           None                              0                  
vulnerabilities        ubuntu:17.10           None                              0                  
vulnerabilities        ubuntu:18.04           None                              0                  
vulnerabilities        ubuntu:18.10           None                              0                  
vulnerabilities        ubuntu:19.04           None                              0   

When the RecordCount of each item is different from 0, it is possible to retrieve the results:

$ anchore-cli image vuln openjdk:10-jdk os
Vulnerability ID        Package                               Severity          Fix                Vulnerability URL                                                   
CVE-2018-1000654        libtasn1-6-4.13-3                     High              None               https://security-tracker.debian.org/tracker/CVE-2018-1000654        
CVE-2018-1000802        libpython2.7-minimal-2.7.15-4         High              2.7.15-5           https://security-tracker.debian.org/tracker/CVE-2018-1000802        
CVE-2018-1000802        libpython2.7-stdlib-2.7.15-4          High              2.7.15-5           https://security-tracker.debian.org/tracker/CVE-2018-1000802        

The list is much longer, but this gives you an impression of what to expect: the CVE identifier, the package it applies to, the severity, whether a fix is available or not and a URL to the vulnerability itself.

When you analyze a Docker image you created yourself and execute the commands as above, you won’t see any results. We used a mykubernetesplanet:0.0.2-SNAPSHOT Docker image we created for a previous post, which contains a Spring Boot MVC application and which is based on the openjdk:10-jdk Docker image. We would expect the same results as above, but it shows us no vulnerabilities. When analyzing our mykubernetesplanet Docker image, we also need to provide the Dockerfile of the Docker image. In order to analyze the mykubernetesplanet:0.0.2-SNAPSHOT Docker image from DockerHub, we clone the Git repository master branch to our home directory and execute the following command:

$ anchore-cli image add mydeveloperplanet/mykubernetesplanet:0.0.2-SNAPSHOT --dockerfile=/home/developer/mykubernetesplanet/Dockerfile
Image Digest: sha256:0d4ec08036b2e2d3fa38130a1d2572d429630868761a9030d0325699fc8a6ddd
Parent Digest: sha256:0d4ec08036b2e2d3fa38130a1d2572d429630868761a9030d0325699fc8a6ddd
Analysis Status: analyzed
Image Type: docker
Image ID: 87b6859f00d3ac1f7f12da4732a6bb214f654a8410af8a9c102c3c5bc2cd5b1c
Dockerfile Mode: Actual
Distro: debian
Distro Version: unstable
Size: 405416787
Architecture: amd64
Layer Count: 9

Full Tag: docker.io/mydeveloperplanet/mykubernetesplanet:0.0.2-SNAPSHOT

When we retrieve the results with the vuln command, we receive the same list as for the openjdk:10-jdk Docker image.

5. Conclusion

We investigated Anchore Engine as a tool in order to help us identify vulnerabilities in our Docker image. With Anchore Engine, we definitely have a tool which will help us to comply to the item Ensure images are scanned and rebuilt to include security patches from the CIS Docker Community Benchmark.