Monday, July 27, 2020

Scanning container images for vulnerabilities using Anchore Engine

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 keep your application up to date but also 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.


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 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, certificates, HTTP headers, try out some XML based attacks maybe, etc. Many challenges, but for scanning container images Anchore Engine seems nice!

No comments:

Post a Comment