OWASP-K8s Security: Insecure Workload Configurations

OWASP Top 10 Kubernetes Vulnerabilities

Kubernetes is the most popular Container-Orchestration Engine used to manage Microservices. It empowers engineers to provision and maintain complicated infrastructures with ease.

However, Kubernetes, in and of itself, is complicated because of the diverse components and wide range of available configurations which are provided through its Manifest files.

Due to this wide variability and complexity, securing the infrastructure is not an easy job. However, we can minimize the attack surface by applying security standards and avoiding well-known misconfigurations which could lead to a serious compromise of the availability and the integrity of the underlying cluster components and services.

In this series of blogs we will focus on OWASP top 10 Kubernetes vulnerabilities, discussing each in a separate blog.

Insecure workload configurations

Kubernetes Manifest files contain many different configurations that can affect the reliability, security, and scalability of a given workload. These configurations should be audited and remediated continuously. — Source

Some examples of high-impact poor configuration decisions are below:

Privileged Containers

The Docker engine utilizes Linux kernel Name Spacing feature and cgroups to isolate container processes from the host machine. However, every container application processes run as root by default as shown below:

This root user is the same as the host root user but with restricted capabilities. These capabilities are a set of limited full-privileged actions that can be delegated to users or processes in order to do a specific job without having to use sudo or access to a superuser.

Let’s take the following three capabilities as an example:

The following screenshot demonstrates how such capabilities work:

For more information about capabilities

By observing the differences between the capabilities between my host machine user and the container, we can clearly see that the container root user is restricted to a set of capabilities:

Host Machine
Non-Privileged Container

Although a non-privileged rooted container is somewhat restricted, compromising one could lead to serious issues. In the screenshot above, we can see that a non-privileged rooted container has a CAP_NET_RAW capability set; It gives the permission to use raw and packet sockets i.e. we can send and receive packets.

tcpdump: prints out a description of the contents of packets on a network interface.

An attacker can use the compromised container as a Bastion host to do a full network scan, then, possibly, o find a way to escape the container.

nmap: Network exploration tool and security/port scanner

Then what about privileged containers?

According to Docker documentation

The --privileged flag gives all capabilities to the container, and it also lifts all the limitations enforced by the device cgroup controller. In other words, the container can then do almost everything that the host can do. This flag exists to allow special use cases, like running Docker within Docker.

This means that the container has all the capabilities of the host root user and that is DANGEROUS.

In this case, if an attacker managed to hop on the server somehow, for example by exploiting a vulnerability on a hosted web application, the compromise of the host machine is just a matter of minutes!

Example:

An attacker can list and mount the host file system using the CAP_SYS_ADMIN capability.

CAP_SYS_ADMIN
Mounted Host File system

Dangerous Kubernetes Manifest

Remediation

  • Avoid running containers as root. Instead, create a low privileged user and run the container using him.
  • Try to avoid --privileged flag and add required capabilities using --add-cap flag.

Docker file example


Run with specified capabilities


Kubernetes Manifest example

Docker In Docker

Some cases require us to build a Docker image inside a Docker container.

Let’s simplify that a bit, suppose you are building a CI/CD pipeline and you have to build Docker files then push them to ECR or Docker hub. Building the Docker files in the job runner will not happen unless Docker is installed and can reach the Docker api.

So it’s a pretty common approach to mount the Docker socket in order to do the job.

We have two options to run a Docker in Docker:

1. Pulling a Docker image and mounting the Docker socket


2. Using a pre-existing Docker in Docker (dind) image.


Using the first method is obviously smarter because we don’t have to append the
--privileged flag.

Although this approach is somehow good for business, it’s very dangerous from the security perspective as an attacker can leverage that mounted Docker socket to break out from the container!

An attacker can easily break out by running a container with a mounted host filesystem, then accessing that container.

Dangerous Kubernetes Manifest

Conclusion

Implementing security standards isn’t an easy job. However, the cost for not putting the effort and time into doing it well is a lot more time and a lot more effort put into fixes. 

So far, we’ve discussed and demonstrated how containers work and how vulnerable their default configurations are. In the second blog, we will talk about supply chain attacks and how we can apply security standards to mitigate risks.

Reviewed by Ibrahim Abdelaziz and Fayez Abu Awad.