1. Using insecure imagesContainers are built using either a parent or a base image. Images are useful for building containers because you can reuse the various components of an image instead of building a container image from scratch. However, like any piece of code, images or their dependencies could contain vulnerabilities.
Recommendation:Image security starts with enforcing strict vulnerability scanning practices and image provenance policies. Consider using a policy that will reject images from being used if they haven’t been scanned in the last 60 days or if they’re sourced from a non-whitelisted image registry.
2. Containers running with the privileged flagAnyone with even a modest knowledge of containers might know what a privileged container is. Containers running with the privileged flag can do almost anything a host can do, run with all capabilities and gain access to the host’s devices. This means that if an attacker breaches a container running with the privileged flag, they can wreak havoc.
Recommendation:As a best practice, don’t run containers with this flag. Instead, use CAP ADD and CAP DROP to provide finer-grained capabilities to your containers.
3. Unrestricted communication between containersContainers need to communicate with each other to accomplish their goals. But due to the number of containers and microservices you might be running and the ephemeral nature of containers in general, implementing networking/firewalling rules that are in adherence to the least privilege principle can be a challenge. Nonetheless, your goal should be to allow containers to communicate to only those containers that are absolutely required in order to minimize your attack surface.
Recommendation:Container orchestration and management tools such as Kubernetes are a great way to implement network controls. Kubernetes, for example, packages containers into Pods and allows DevOps teams to implement networking policies that can restrict Pod-to-Pod communication to what’s required. You can do this by first observing the behavior of the application to determine which communication paths are necessary for the application to function and then create network policies that are essentially whitelists for those networking pathways.
4. Containers running rogue or malicious processesIn a sprawling environment with a container having an average lifespan of hours or even minutes, monitoring running container processes can be particularly challenging. In other words, the rapid churn of containers make it near impossible for mere mortals to monitor which container processes are running at any given time, let alone identify unnecessary or malicious processes.
Recommendation:Instead of waiting for a successful breach to notify you that a malicious process was running in a container and affecting container security, limit the number and types of processes that can run. There are two ways to mitigate this risk. You can use Docker’s CAP ADD feature to add only those Linux capabilities necessary for a container to run properly and achieve its goal and use CAP DROP to remove all unnecessary capabilities. As an additional mitigation step, you can also set PID limits such that you’re limiting your container to only run a set number of processes that conforms with what’s required for the container to achieve its goal. This will not only prevent fork bombs but also ensure that malicious processes such as reverse shells and remote code injections are being prevented from running.
5. Containers that are not properly isolated from the hostIt's a double-edged sword when it comes to container security. Their immutable nature, combined with their short life span and limited functionality, offers several security benefits. Containers, however, can also be a vector for attacking the underlying host. We mentioned earlier that containers running the privileged flag pose this risk. There are many other misconfigurations that can put the underlying host at risk.
Recommendation:There are several steps you can take to further isolate containers from the host. Below are just two examples:
- Don’t share the host’s network namespace. Doing so could put you at risk of a container shutting down the Docker host.
- Don’t share the host’s process namespaces. Doing so would allow a container to see all of the processes running on a host system, which leaves the processes on the host at risk of being manipulated or shut down.
About the Author: