by Michael Wager

Containers have become an essential part in modern software development. The technology greatly simplifies and enhances the way software is developed and deployed to production. DevOps – the combination of development and operations would be hard without them. This success leads to a giant number of public container image repositories and unfortunately also potential issues regarding security. Most of these containers contain many components not needed in production (e.g. shells, package managers, binaries/files with special permissions etc) and these components often have high or even critical vulnerabilities which could be exploited by malicious attackers. This report from Sysdig claims „that 75% of containers have “high” or “critical” patchable vulnerabilities“.

This blogpost will give a quick introduction to container security best practises and also looks at an interesting approach to reduce the attack surface by removing not needed components. It does not matter if you are using Kubernetes (K8s) or running your containers inside a public or private cloud.

 

Best practises

Typical vulnerabilities related containers are for example related to older – and therefore vulnerable – versions of docker (e.g. directory traversal) or vulnerabilities related binaries installed inside the image (See this link from snyk for more information about Docker Security – Challenges & Best Practices.) Also, snyk is the default scanner used behind the scenes when executing `docker scan`.

snyk-critical-container-scan-finding

Example output of a critical container vulnerability affecting the curl package from executing `docker scan`

 

Standards and relevant documents explaining potential security concerns in containers and also mitigations against them are the OWASP Container Security Verification Standard, the Application Container Security Guide from NIST and the BSI Grundschutz module SYS.1.6  about Containerization.
Although container security is a complex topic, fortunately there are certain low hanging fruits which are very easy to implement and can have large impact on security. Let’s have a look at some of them.

1. Do not run your containers as root

Docker containers run as root by default. But by using the USER command in your Dockerfile it is quite simple to switch to a non-root user.

2. Use Secure Container Registries

Just like as described in our post about vulnerable components inside your software supply chain, there may be vulnerable container images in certain public registries. Therefore you should only use trusted registries or even better, your own private registry.

3. Use minimal base images

There are images like „slim“ or „alpine“ you may use as base. They contain way less components than the large full blown default images and are therefore reducing the attack surface. Canonical, for example, provides Ubuntu base images free from high or critical vulnerabilities in a timely manner – commercial support from other vendors might also be an option in your organisation. The less components an image contains, the lower the likelihood of severe vulnerabilities.

4. Scan your containers for vulnerabilities

There are tools which scan your images for known vulnerabilities. You should use them and consider to fail the pipeline, e.g. in case there are high or critical findings. Some tools you could consider:

  • Trivy (Open source, free)
  • Snyk (Free for 100 tests / month)
  • PrismaCloud and twistcli (Enterprise ready)

5. Monitor your containers in production

After your containers are running in production, new vulnerabilities may be found. That’s why you should also monitor your containers and get notified when new vulnerabilities are discovered.

Unfortunately, scanners often report a lot of findings and false positives (or vulnerabilities that aren’t actually a risk to your specific software or service). It can be hard to decide if vulnerable components are really an issue. A better approach would be not to install so many components in the first place. Enter „distroless“!

6. Consider „Distroless“ images

A very interesting concept is coming from google and is called „distroless„. What they mean by distroless are images that contain only the application code and its runtime dependencies. The open source project provides ready to use images for Java, Node.js, C# and Python. Others may be built using either their „static“ base images or using google’s build tool Bazel. So you get small, production-ready images with only the dependencies you need, but it is important to note that development teams have to have very good knowledge about the underlying functionality of a linux system when using distroless. For example, a node.js application with a dependency to a glic based binary needs the app team to make certain non-trivial changes to the image build process. Another example regarding the PHP runtime, it can get quite complex when it comes to building your own images using Bazel. Other projects like “Chisel” from Canonical, the open source project behind Ubuntu, are currently trying to take this appoach to the next level. We have consulted companies regarding this topic and brought secure distroless images to production.

Conclusion

Just because your applications are running in containers does not mean they are secure – but there are simple ways to add more security. For our customers, we are always looking for efficient solutions to automate as much as possible and to shift security left. Our security experts can consult you on the topic and also help you selecting and integrating container scanning tools into your development lifecycle.

If you have questions, contact us at cybersecurity@secure-io.de