Is your container truly secure?
Containerised application development is a common engineering practice. However, putting your code in a container doesn’t make it secure out of the box. Yup! you heard it right. And believe me, you are not the only one! Count me on this list as well.
The moment of realisation dawn upon me when I had a conversation with the AppSec engineer (Aaron) about it. That’s how the conversation went (at least what I can recall).
Me: Hey Aaron, how are you doing mate! Aaron: Good OZ, how about it. Me: Same old same old. Hey, I would like to run a few things with you regarding the new service I am working on. Aaron: Sure! Shoot Me: Well! it will be quick as I have it all covered (me showing off). It is a containerised application so security wouldn't be an issue... Aaron: Hold it right there. What makes you say that? Me: (caught up a bit surprised) aa.. as I said it is CONTAINERISED. Aaron: News flash OZ, putting your code in a container doesn't make it secure out of the box. At worst, it give you an illusion of security which can catch you off-guard. Me: I have to be honest with you, I wasn't aware of it.
This conversation was a bit embracing but I have been a developer long enough to know what I don’t know and never shy to admit it. This conversation made me think about the security aspects of a container-based application. It started a journey to explore the notion of security in the context of a container based applications.
In this blog, i will share a quick summary of useful stuff that helped me create a better, more secure application.
Without further ado!
Drum rolls please!!
5 simple ways to improve your container security
1: Smaller container = Smaller attack surface
The container should only contain enough support structure (OS features, tools, libraries, running processes, etc) to run the desired application as expected. Everything else should not be part of your container ecosystem.
Given the hereditary nature of the container framework like Docker. You are not only inheriting the functionality but also the security vulnerabilities. It is important to evaluate the base image carefully.
One possible solution is to use Docker Slim which minify the image and create security profiles (more on it later). Check out this short video for details
This principle will guide you to adopt micro service based architecture. A smaller size image is easier to sync with remote repositories and improves the deployment time.
2: Contain the container
Control what the container can see and do!
Operating System (OS) provides a large number of system calls to perform various OS / Kernel related operations like modifying I/O privileges or profiling etc.
It is important to control the sys commands a container can run. By default, Docker applies a “Seccomp” profile which whitelists a small subset of commands. However, you can take a step further to create a tighter list.
Ensure that the container is running in the least possible privilege settings. Aside from Seccomp, you can isolate containers (limit what container can see or do) by running it in the context of an unprivileged user. DON’T USE ROOT USER unless there is a clear case for it. Remember: root user in a container is a root user outside.
3: Run vulnerability scans
Containers are created hierarchically. Your container uses a base image that may inherit from any other and so on. This establishes a system largely based on vendors or third-party routines. With this model, you will inherit the good, the bad and the ugly.
If your base image has a security vulnerability, guess what!! it has become your application’s security vulnerability. This serious problem leads to the rise of security vulnerability tools like Twist Lock, Anchore, Clair, etc. There are a lot of options out there…. open source, proprietary. In fact, Docker Enterprise also provides scan features when you push your docker image to the registry. Personally, I am using Twist-Lock and it works like a charm.
To put it simply the main purpose of the security vulnerability tool is to perform a static analysis of the dependency tree and identify any potential security loopholes by comparing it with the database of known security issues.
The scan results may look like this:
Recently, I used these tools extensively with the service I am working on. I will share my insights about comparative analysis of JRuby image on Open JDK, AWS Corretto, Adopt JDK and Azul Zulu. Let’s keep this topic for another blog 🙂
Pro tip: Integrate Security Vulnerability scan in your CI/CD pipeline and make it a blocker for your deployment.
4: Apply security best practices
I got to have this one on my list. It is always handy to have a tool for course correction, especially for a lazy lousy developer like me.
Lads! please welcome “Docker Bench Security“. Shout out to Thomas & Diogo for this Swiss army knife of a tool for docker developers.
When you run this tool on your container or image, it executes a script to check common best practices (like the stuff we discussed in point #2) and give you a quick summary and report. Running this tool while developing apps will ensure that all bases are covered.
# clone the repo and run it like this. sh docker-bench-security.sh -i <Container or Image Name>
One area of improvement for this script. I would love to have something like this as part of my continuous development process or as a plugin to my IDE. Like to know your thoughts. If I get 10 or more yeas, I will work on it.
5: Use signed images
This may sound obvious and common knowledge but more often than not, it slips through the cracks. You can blame the triviality of the task but it can be consequential.
As a general rule of thumb, you should know the owner of the docker image you are using (as a base or an element of a composite). This is where “signed images” come into the picture. It revolves around the idea of using Digital Signature to ensure integrity.
The author should sign the images and the consumer should use one. Let’s break this down a bit.
– Generate “root key” using docker trust. See the link below for more details
– Use the root key to create a repository tag pair using the docker trust signer. The public key will be shared by Docker Notary service.
– Use the private key to sign the tag.
– No need to signed all the tags. However, you should sign the ones your consumers will use. For example, latest or LTE tag, etc.
– Make this part of your build process to avoid manual work.
– Think about whether it makes sense for you to make it an Official Docker container. It is not straight forward, you need to comply with the list of requirements that may affect your timelines.
More details here.
– Use official or user signed images.
– Enforce this check on the docker daemon to avoid non-compliance.
Application security is a vast and evolving field. I am not an expert in it by any means. The main purpose of this blog is to share my experience and things I learned by reviewing articles, documentation and going through many iterations of trial and error.
I hope it helps in your journey! Let me know your feedback.
Stay Safe! Stay Hopeful!