If you're familiar with microservices and containers, you know that the adoption of these technologies is fueled by development efficiency, manageability, and infrastructure flexibility. However, organizations that have deployed microservice- and container-based applications are beginning to realize that this new architecture also helps improve the overall security of their applications.

This is because of two fundamental characteristics of containers: They are declarative and immutable.

Declarative security

For Docker images, developers make a "Dockerfile" along with the image. The Dockerfile stipulates how the image is put together, where the layers came from, and even which ports are open.

"Docker compose" is another declarative file, which specifies how different containers belong to the same application and how the individual services fit together.

Let's take a look at a sample Dockerfile:

FROM Ubuntu

MAINTAINER McAuthor

RUN apt-get update

RUN echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | tee /etc/apt/sources.list.d/mongodb.list

RUN apt-get install -y mongodb-10gen

RUN mkdir -p /data/db

EXPOSE 27017

This Dockerfile stipulates the making of a MongoDB on Ubuntu container.

The first two lines in the file state that this image is built from an Ubuntu base image whose author is "McAuthor." The third line instructs on getting the latest update of Ubuntu. The fourth line instructs on where to download the MongoDB repo and install it on Ubuntu, and the fifth line installs it. The sixth line specifies where the data directory is for MongoDB, and the last line says Port 27017 should be open.

Because the Dockerfile is part of the image, in runtime you can have a tool that parses the Dockerfile automatically and you will know

How the image is constructed—which base image and which layers on top

Whether the base image is updated

Which ports should be open

Where it should be writing to

Then, you could have monitoring and policy enforcement capabilities that compare the ongoing behavior of the container versus what is expected (i.e., declared), and execute enforcement actions if you detect deviation. For example, if you see that other ports are open beyond 27017, that could be an indication of compromise or misconfiguration and is an event that is worth reporting.

Similarly, if you see from the Dockerfile that "adp-get update" was not included, perhaps the image included an obsolete base image of Ubuntu, in which case you should not permit this container to be deployed. In addition, you could process Docker compose to understand whether this container is part of a distributed application and whether it should be communicated with others in a specific pattern.

More importantly, once you know that this container is a MongoDB on top of Ubuntu, you can automatically construct policies to govern the safe execution of the container. Which leads us to the importance of the immutability.

Immutability

Much has been written on the immutable aspect of containers. Docker containers are made of read-only layers with the exception of one writeable layer, which contains all the changes made dynamically. At the end of the execution, the changes in the writeable layer has to be explicitly committed via “docker–commit” to be saved, otherwise it goes away and will not persist.

Content inside a container is primarily read-only and immutable. This is critical, because this feature allows you to automatically build runtime policies for that specific app, as well as governing behaviors for storage, networking, process, and even system calls.

More specifically, knowing that it is a MongoDB app and that it should not change dynamically throughout execution, you can safely deduce that the app will write to a specific data directory; it will have a particular set of processes indicative of a MongoDB app; it only talks to a specific application server; and it will initiate a specific set of system calls. This knowledge enables you to look for and detect behavior deviations quickly and accurately.

An example

Let’s say this MongoDB app has a zero-day vulnerability and is being exploited. You should be able to use the above characteristics to quickly detect the compromise and thwart attempts to further the intrusion. More specifically:

Sys call anomaly : If the MongoDB app accesses an unusual, or disallowed, system call, such as "chroot," you could deduce with a certain degree of confidence that a successful exploit or an attempt to exploit has happened.

: If the MongoDB app accesses an unusual, or disallowed, system call, such as "chroot," you could deduce with a certain degree of confidence that a successful exploit or an attempt to exploit has happened. Process anomaly : Since the exploit most likely will fork new processes and may execute a shell command with admin rights, you can use the expected process map of the container to detect the presence of unwanted processes, which is another indication of potential compromises.

: Since the exploit most likely will fork new processes and may execute a shell command with admin rights, you can use the expected process map of the container to detect the presence of unwanted processes, which is another indication of potential compromises. Network anomaly: Suppose the attacker downloads a malware kit next. The networking profile of the MongoDB container may stipulate that it should only talk to a particular application server or a particular pattern of inter-container communication. The fact that this container is contacting either a known bad IP or an external IP that it had no business contacting in the first place is once again an indication that something is wrong.

Note that much of this can be done automatically via parsing of the Dockerfile and Docker compose, and with knowledge of what common applications are supposed to do. Developers don't have to do any extra work, and the security team doesn’t have to build and manage complex rules for each app.

Security assurance, even at runtime

Traditional runtime security tools, such as firewalls and intrusion prevention systems (IPS), often require a human to manage and configure rules and policies to protect applications. Even then, it is done with little knowledge of the inner workings of the application. As your app landscape grows and your dev cycles shrink, this approach simply doesn’t scale.

Docker makes it easy to ship and run your apps. But its other fundamental characteristics, such as declarative runtime properties and immutability, enable assertions to be made about the application's runtime behavior. This ultimately leads to powerful anomaly- and policy-enforcement capabilities in runtime.

Image credit: Flickr

Keep learning