Applications nowadays, are usually deployed inside containers. A container consists of libraries and tools which allow the application to run inside. Since there can be exploitable vulnerabilities, it is not only important to take security into account for your application, but also for the container it runs in. There are various tools available to scan container images for those vulnerabilities. Having little experience with them, but recognizing the importance of having such a tool, I decided to give Anchore Engine a try. Why? Because it appeared popular when looking for tools, it has an open source variant which I can appreciate and it was very easy to get started with. In addition, it provides several integration options which make using it easy, such as a Jenkins plugin and a Kubernetes Admission Controller. In this blog post I’ll describe my first impression/experiences.
Getting started
Anchore Engine provides various ways in which you can install it here. I decided to follow the Docker Compose quickstart instruction here. I also created a Katacoda scenario here so you can try it out for yourself! I will not repeat the entire quickstart since it is straightforward, but provide a quick example:
#Download and run docker-compose file
curl https://docs.anchore.com/current/docs/engine/quickstart/docker-compose.yaml > docker-compose.yaml
docker-compose up -d
#Check status of feeds (first time updating can take a while)
docker-compose exec api anchore-cli system feeds list
#Block until complete
docker-compose exec api anchore-cli system wait
#Start analysis
docker-compose exec api anchore-cli image add openjdk:11.0.6-jre-slim
#get status
docker-compose exec api anchore-cli image list
#Show vulnerabilities
docker-compose exec api anchore-cli image vuln openjdk:11.0.6-jre-slim all
This gives you a list of vulnerabilities of the image you indicated you wanted scanned. For example for openjdk:11.0.6-jre-slim:
If you want to scan multiple images, for example to determine the most secure JRE 11.0.6 image, you can do the following in a Bash script:
strings=(
openjdk:11.0.6-jre-buster
openjdk:11.0.6-jre
openjdk:11.0.6-jre-slim-buster
openjdk:11.0.6-jre-slim
openjdk:11.0.6-jre-stretch
adoptopenjdk:11.0.6_10-jre-openj9-0.18.1
adoptopenjdk:11.0.6_10-jre-hotspot
adoptopenjdk:11.0.6_10-jre-openj9-0.18.1-bionic
adoptopenjdk:11.0.6_10-jre-hotspot-bionic
adoptopenjdk/openjdk11:jre-11.0.6_10-ubuntu
adoptopenjdk/openjdk11:jre-11.0.6_10
adoptopenjdk/openjdk11:jre-11.0.6_10-ubi-minimal
adoptopenjdk/openjdk11:jre-11.0.6_10-ubi
adoptopenjdk/openjdk11:jre-11.0.6_10-debianslim
adoptopenjdk/openjdk11:jre-11.0.6_10-debian
adoptopenjdk/openjdk11:jre-11.0.6_10-centos
adoptopenjdk/openjdk11:jre-11.0.6_10-alpine
mcr.microsoft.com/java/jre:11u6-zulu-alpine
mcr.microsoft.com/java/jre:11u6-zulu-centos
mcr.microsoft.com/java/jre:11u6-zulu-debian8
mcr.microsoft.com/java/jre:11u6-zulu-debian9
mcr.microsoft.com/java/jre:11u6-zulu-debian10
mcr.microsoft.com/java/jre:11u6-zulu-ubuntu
azul/zulu-openjdk-alpine:11.0.6-jre
)
for i in "${strings[@]}"; do
docker-compose exec api anchore-cli image add "$i"
done
Processing results
Now you have to wait a while for all the images to be scanned. If it’s done, you can process the data.
strings=(
openjdk:11.0.6-jre-buster
openjdk:11.0.6-jre
openjdk:11.0.6-jre-slim-buster
openjdk:11.0.6-jre-slim
openjdk:11.0.6-jre-stretch
adoptopenjdk:11.0.6_10-jre-openj9-0.18.1
adoptopenjdk:11.0.6_10-jre-hotspot
adoptopenjdk:11.0.6_10-jre-openj9-0.18.1-bionic
adoptopenjdk:11.0.6_10-jre-hotspot-bionic
adoptopenjdk/openjdk11:jre-11.0.6_10-ubuntu
adoptopenjdk/openjdk11:jre-11.0.6_10
adoptopenjdk/openjdk11:jre-11.0.6_10-ubi-minimal
adoptopenjdk/openjdk11:jre-11.0.6_10-ubi
adoptopenjdk/openjdk11:jre-11.0.6_10-debianslim
adoptopenjdk/openjdk11:jre-11.0.6_10-debian
adoptopenjdk/openjdk11:jre-11.0.6_10-centos
adoptopenjdk/openjdk11:jre-11.0.6_10-alpine
mcr.microsoft.com/java/jre:11u6-zulu-alpine
mcr.microsoft.com/java/jre:11u6-zulu-centos
mcr.microsoft.com/java/jre:11u6-zulu-debian8
mcr.microsoft.com/java/jre:11u6-zulu-debian9
mcr.microsoft.com/java/jre:11u6-zulu-debian10
mcr.microsoft.com/java/jre:11u6-zulu-ubuntu
azul/zulu-openjdk-alpine:11.0.6-jre
)
echo Unknown,Critical,High,Medium,Low,Negligible,Image
for i in "${strings[@]}"; do
docker-compose exec api anchore-cli image vuln "$i" all | awk 'NR>1{print $3}' | sort -n | uniq -c >parse.txt
UNKNOWN=`cat parse.txt | grep Unknown | awk '{print $1}'`
CRITICAL=`cat parse.txt | grep Critical | awk '{print $1}'`
LOW=`cat parse.txt | grep Low | awk '{print $1}'`
MEDIUM=`cat parse.txt | grep Medium | awk '{print $1}'`
HIGH=`cat parse.txt | grep High | awk '{print $1}'`
NEG=`cat parse.txt | grep Negligible | awk '{print $1}'`
if [ -z "$UNKNOWN" ]; then
UNKNOWN=0
fi
if [ -z "$CRITICAL" ]; then
CRITICAL=0
fi
if [ -z "$LOW" ]; then
LOW=0
fi
if [ -z "$MEDIUM" ]; then
MEDIUM=0
fi
if [ -z "$HIGH" ]; then
HIGH=0
fi
if [ -z "$NEG" ]; then
NEG=0
fi
echo $UNKNOWN,$CRITICAL,$HIGH,$MEDIUM,$LOW,$NEG,"$i"
done
This provides a nice comma separated list which you can use in your favorite spreadsheet for some visualization
Next, you can draw some conclusions like
- Newer OS versions are more secure
- Alpine does better than Debian/Ubuntu. Debian/Ubuntu does better than RHEL/CentOS
- Slim versions do slightly better than not so slim versions
- No OpenJDK JRE 11.0.6 images scanned have critical vulnerabilities. Very few have high severity issues
- In the OpenJDK images, when a new version is released, the underlying libraries and tools are usually also updated, reducing the number of vulnerabilities in newer versions.
Finally
I was surprised (even though I was trying out a quickstart) in how little time I could perform a vulnerability scan of a list of images. The integration options Anchore Engine provides, also seem powerful, although I did not try them out yet. There seems little reason not to cooperate a scan like this in your CI/CD environment. I suggest you give it a try!
This is just an example of a security related challenge. The container platform itself runs on an OS which you should check. Kubernetes and its native components can have vulnerabilities. Of course you should also keep an eye on already deployed images, since new vulnerabilities can be found. I did not scan an image containing an actual application. This might have provided some additional insights. You should perform scans on your source code dependencies (see for example the OWASP dependency check here) and on the code itself (see here). Also outside of your source code it is advisable to do some security related integration tests, such as checking your HTTPS connection (e.g. cipher suites), certificates, HTTP headers, try out some XML based attacks maybe, etc. Many challenges, but for scanning container images Anchore Engine seems nice!