Improving security using software containers
In this section, we are going to introduce some of the features found on container platforms that help improve application security.
If we keep in mind how containers run, we know that we first need a host with a container runtime. So, having a host with just the software required is the first security measure. We should use dedicated hosts in production for running container workloads. We do not need to concern ourselves with this while developing, but system administrators should prepare production nodes with minimal attack surfaces. We should never share these hosts for use in serving other technologies or services. This feature is so important that we can even find dedicated operating systems, such as Red Hat’s CoreOS, SuSE’s RancherOS, VMware’s PhotonOS, TalOS, or Flatcar Linux, just to mention the most popular ones. These are minimal operating systems that just include a container runtime. You can even create your own by using Moby’s LinuxKit project. Some vendors’ customized Kubernetes platforms, such as Red Hat’s OpenShift, create their clusters using CoreOS, improving the whole environment’s security.
We will never connect to any cluster host to execute containers. Container runtimes work in client-server mode. Rather, we expose this engine service and simply using a client on our laptop or desktop computers will be more than enough to execute containers on the host.
Locally, clients connect to container runtimes using sockets (/var/run/docker.sock
for dockerd
, for example). Adding read-write access to this socket to specific users will allow them to use the daemon to build, pull, and push images or execute containers. Configuring the container runtime in this way may be worse if the host has a master role in an orchestrated environment. It is crucial to understand this feature and know which users will be able to run containers on each host. System administrators should keep their container runtimes’ sockets safe from untrusted users and only allow authorized access. These sockets are local and, depending on which runtime we are using, TCP or even SSH (in dockerd
, for example) can be used to secure remote access. Always ensure Transport Layer Security (TLS) is used to secure socket access.
It is important to note that container runtimes do not provide any role-based access control (RBAC). We will need to add this layer later with other tools. Docker Swarm does not provide RBAC, but Kubernetes does. RBAC is key for managing user privileges and multiple application isolation.
We should say here that, currently, desktop environments (Docker Desktop and Rancher Desktop) also work with this model, in which you don’t connect to the host running the container runtime. A virtualized environment is deployed on your system (using Qemu if on Linux, or Hyper-V or the newer Windows Subsystem for Linux on Windows hosts) and our client, using a terminal, will connect to this virtual container runtime (or the Kubernetes API when deploying workloads on Kubernetes, as we will learn in Chapter 8, Deploying Applications with the Kubernetes Orchestrator).
Here, we have to reiterate that container runtimes add only a subset of kernel capabilities by default to container processes. But this may not be enough in some cases. To improve containers’ security behavior, container runtimes also include a default Secure Computing Mode (Seccomp) profile. Seccomp is a Linux security facility that filters the system calls allowed inside containers. Specific profiles can be included and used by runtimes to add some required system calls. You, as the developer, need to notice when your application requires extra capabilities or uncommon system calls. The special features described in this section are used on host monitoring tools, for example, or if we need to add a new kernel module using system administration containers.
Container runtimes usually run as daemons; thus, they will quite probably run as root users. This means that any container can contain the host’s files inside (we will learn how we can mount volumes and host paths within containers in Chapter 4, Running Docker Containers) or include the host’s namespaces (container processes may access host’s PIDs, networks, IPCs, and so on). To avoid the undesired effects of running container runtime privileges, system administrators should apply special security measures using Linux Security Modules (LSM), such as SELinux or AppArmor, among others.
SELinux should be integrated into container runtimes and container orchestration. These integrations can be used to ensure, for example, that only certain paths are allowed inside containers. If your application requires access to the host’s files, non-default SELinux labels should be included to modify the default runtime behavior. Container runtimes’ software installation packages include these settings, among others, to ensure that common applications will run without problems. However, those with special requirements, such as those that are prepared to read hosts’ logs, will require further security configurations.
So far in this chapter, we have provided a quick overview of the key concepts related to containers. In the following section, we’ll put this into practice.