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
$ 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
$ 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 $ ANCHORE_CLI_USER=admin $ ANCHORE_CLI_PASS=foobar $ export ANCHORE_CLI_URL $ export ANCHORE_CLI_USER $ export ANCHORE_CLI_PASS
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.
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.