“Reduce your attack surface” is a common point of advice about container security... but what does it actually mean to reduce the attack surface of a container?
The answer is to avoid including code or configurations that make the container ripe for exploitation by attackers. For that reason, knowing what not to include in a container is a critical step toward container security.
With that reality in mind, here’s a look at five things to avoid when creating a container. These insights are informed, in part, by SlimAI’s analysis of security problems with popular public container images.
You probably already know that you shouldn’t run containers in privileged mode, which means allowing them to run as root.
However, another way to run containers in privileged mode is to create a Dockerfile that includes an environment setting like this:
This setting has basically the same effect as running the container in privileged mode. It tells your container to run processes as the root user.
Now, you may be thinking this isn’t a big deal, because if Docker doesn’t execute the container as the root user, the operating system should prevent anything inside the container from running as root. That’s true, but it’s still certainly not a best practice to define root users within a container. Doing so means you’re only one --privileged flag away from accidentally allowing your container to run as root – and, in turn, making it much easier for attackers to gain root-level access to the host.
Another obvious no-no for running containers is to avoid storing secret data in plaintext inside the container image.
Most people know they shouldn’t do this, of course. However, most people don’t want to take the time to remove this data, so they will include plaintext passwords or encryption keys in Dockerfiles during development, assuming they’ll strip them out later. Of course, that doesn’t happen in 100 percent of cases.
So, resist the temptation to do the easy thing by including unsecured secrets in your containers. Instead, use tooling like those provided by Docker and Kubernetes for securely sharing secret data with containers.
If you think no one makes the mistake of shipping containers with sensitive secret data, think again. This was the vector for attacks such as one that affected Codecov, where attackers used credentials that they found in a Docker image to breach a Codecov tool.
This statement may raise some eyebrows, but I’ll go ahead and make it: as a best practice, you should avoid running a shell inside a container unless you specifically need to.
That may sound strange because most container base images include shells by default, and it’s pretty common to use commands like docker exec to interact with those shells.
On the other hand, in many cases, you don’t actually need a shell inside your container. If you’re shipping a production Web app, for example, you just need the app to execute. There’s no reason for a shell to run alongside it. And if you do provide a shell, you make it that much easier for someone to break in, run arbitrary commands, and perhaps even execute things like reverse shell attacks.
I’m not saying that running a shell when it’s not necessary will instantly cause you to be hacked. In most cases, you’ll probably be OK. But I am saying that it’s a best practice to avoid running a shell if you don’t actually need one.
Here’s another statement that some people may take issue with: you should never include a package manager inside a container unless you strictly need it.
On the other hand, having a package manager like an apt available inside your container is useful if you need to install additional software at runtime. This is why many base images include package managers. And in general, having a package manager comes in handy if you need to install something manually once a container is running.
The problem, however, is that running a package manager also makes it much easier for attackers to escalate attacks once they are inside a container. Being able to apt-get a bunch of packages is a great way to install the extra tools you need to break out of a container you’ve compromised – which is part of what happened during the Kinsing malware exploit.
The larger your container image, the longer it will take to scan. SlimAI’s container image analysis found that images that are 1 gigabyte in size take 6 times longer to scan than 200-megabyte images.
Now, a larger image size isn’t a security risk per se. However, large images that slow down scans complicate security operations. They increase the risk that someone will push a container into production before scanning it because they don’t want to wait on the scan. At a minimum, they slow down your CI/CD pipeline.
Maximizing container security means minimizing your containers’ attack surface. And while it may not always be convenient to strip out the unnecessary bits of container images, the reality is that anything you don’t specifically need – including resources like shells and package managers – is a security risk that you can easily avoid by being strategic about what you ship in your containers.