A container is a unit of software that brings code and all of its dependencies together, making it possible to run the application quickly and reliably in different computing environments. Docker containers are standalone, lightweight software packages that include everything you need to run an app, including code, system tools, system libraries, runtime, and settings. Container images become containers at runtime, and containerized software runs the same way in any infrastructure. They’re called containers because they’re contained — they isolate the software from the environment, and they’re designed to run uniformly. Kubernetes, of course, is an open-source container orchestration system. It automates software deployment, scaling, and management. Kubernetes uses pods as the smallest deployable unit, but every pod must contain one or more containers. In other words, you can’t really have Kubernetes without containers.
That’s why it’s so important to understand container security and how the permissions of your containers can impact your Kubernetes workloads. The National Security Agency’s Kubernetes Hardening Guide specifically advises organizations to use containers that have been built to run applications as non-root users. Frequently (and by default), many container services run as the privileged root user, even though these apps don’t require privileged execution. As you’re working to harden your Kube environment, you need to spend some time investigating which containers are running as root and then make changes to prevent root execution so you can limit the impact of a container compromise.
Polaris is an open source project that validates Kubernetes configuration. It includes a built-in check specifically for detecting containers that are allowed to run as root. Polaris is built into Fairwinds Insights (there’s a free tier available for environments up to 20 nodes, two clusters, and one repo if you aren’t already using Insights), which allows Insights users to see exactly which workloads in your clusters have permission to run as root. You can use Polaris without Insights as well, and it will help you ensure that your containers are running with minimal privileges. Polaris policies can help you avoid privilege escalation and make sure that you are using read only file systems wherever possible. Insights allows you to run the same policy for finding containers that are allowed to run as root in your CI/CD pipeline; this helps you ensure that infrastructure-as-code changes don’t introduce new resources that have the ability to run as root.
Insights collects data from all of the Reports installed and displays them together in a dashboard. If you look at the Action Item called
should not be allowed to run as root, you can see in the screenshot that it’s a security issue, the severity is high, and Polaris detected the issue.
In the Action Items, Insights includes information about the type of Kubernetes object, the namespace, and the name of the Kubernetes object. You can also click on the title to see this information. Now you easily identify the resource that you need to change. Insights also displays a description section, which includes a short explanation of the action item.
The remediation section provides steps you can take to remediate the action item. For this action item, the remediation step says that you need to set securityContext.runAsNonRoot=true and it provides examples of what that looks like.
To add the security context parameter, you usually want to go to the source of your resource definition. This may be a Helm values file or a flat manifest file that you have in your repo, depending on how you deploy things in your environment.
In the walkthrough video, I edited a pod definition file on my local machine, the
pod with nonroot.yaml file. As indicated in the remediation step, you can add a security context at the pod or the container level. It's best to add this security context at the pod level because it will then be the default for all the containers. If you have a compelling reason not to set it at the pod level, you will need to set it for every container in the pod.
So, what happens if you try to run a pod with this security context key defined? In the video, I do a
k apply -f pod-with-nonroot.yamlto deploy the pod. (I have kube control aliased to K on my system.) When I hit enter, you can see that it says the pod has been created. But if you look at the pod, the status says,
create container config error. That means the pod is not actually running. You can find out why by doing a
k describe po pod-run-as-non-root.
In the events that display, you can see a message that says,
Error: container has runAsNonRoot and image will run as root. The API server will no longer start this pod because of that security context setting. So that’s good — you don’t have an over-permissioned container running. But it’s bad because now your container isn’t running at all.
Now you have a container that can't run in your cluster and you need to know how you can fix it. In your Docker file, you will probably want to create your own user and/or group and switch to that entity before you run your entrypoint or command arguments.This will make the container run as the user specified in your Dockerfile.
Alternatively, you can use the
RunAsUser directive in your container specification. The value for this key must be a user ID, and the user ID also needs to be a user that exists in the container. Keep in mind that when you run your container as NonRoot you will want to make sure that the user you create has the right permissions to run the processes within your container.
Regardless of where you define the alternate non-root user, the recommendation is still to set the security context to
RunasNonRoot to increase the security of the container.
Once you have updated your pods and containers to ensure that they no longer have the ability to run as root across your workloads, consider turning on Polaris in the Insights Admission Controller. This provides the strongest defense against workloads that run as root in your Kubernetes environment with the ability to block those workloads from entering your cluster in the first place. For workloads that really do need root access (there shouldn’t be many), you can configure Insights to allow specific exemptions. Using an allow-list for this purpose and denying the ability to run as root by default will help you make sure that your containers are running as securely as possible.