Guest post by Ajmal Kohgadai, originally published on StackRox

By now most of us have heard about the role human error plays in causing data breaches. The Capital One breach from July is just the latest in a long line of security incidents that can trace their success back to a misconfigured infrastructure or security setting. As organizations accelerate their use of containers and Kubernetes and move their application development and deployment to cloud platforms, preventing avoidable misconfigurations in their environment becomes increasingly crucial.

Fortunately, most organizations understand that containers and Kubernetes are just like previous waves of infrastructure, where security starts with a securely configured infrastructure. In a recent survey of IT and security practitioners, respondents identified user-driven misconfigurations as their biggest concern for container security.

In this article, we will take a deep dive into key Kubernetes security configurations and recommended best practices you should follow.

It should be noted however that ensuring adherence to these best practices requires more than just knowing what they are. The level of success you have in consistently following these recommendations will also be determined by the degree to which you can automate the process of checking your environment for misconfigurations.

That’s because in a sprawling Kubernetes environment with several clusters spanning tens, hundreds, or even thousands of nodes, created by hundreds of different developers, manually checking the configurations is not feasible. And like all humans, developers can make mistakes – especially given that Kubernetes configuration options are complicated, security features are not enabled by default, and most of the community is learning how to effectively use components including Pod Security Policies and Security Context, Network Policies, RBAC, the API server, kubelet, and other Kubernetes controls.

As you and your teams come up to speed on all the details of Kubernetes security, follow these best practices to build a strong foundation:

1. Update Kubernetes to the latest version

If you haven’t already done so, update your Kubernetes deployments to the latest version (1.16), which includes several new and exciting features. Every new release is typically bundled with a host of different security features. Be sure to check out our blog post that highlights 7 reasons why you should upgrade Kubernetes to the latest version.

2. Use Pod Security Policies to prevent risky containers/Pods from being used

PodSecurityPolicy is a cluster-level resources available in Kubernetes (via kubectl) that is highly recommended. You must enable the PodSecurityPolicy admission controller to use it. Given the nature of admission controllers, you must authorize at least one policy – otherwise no pods will be allowed to be created in the cluster.

Pod Security Policies address several critical security use cases, including:

Some of these attributes can also be controlled via securityContext. You can learn more about security context here. However, it’s generally recommended that you shouldn’t customize the pod-level security context but should instead use Pod Security Policies (see Recommendation #6 on how to apply these controls).

You can learn more about Pod Security Policies here.
You can learn more about admission controllers here and here.

3. Use Kubernetes namespaces to properly isolate your Kubernetes resources

Namespaces give you the ability to create logical partitions and enforce separation of your resources as well as limit the scope of user permissions. You can learn more about namespaces here.

4. Use Network Policies to segment and limit container and pod communication

Network Policies are used to determine how pods are allowed to communicate. Check out our blog post that takes a deep dive into building secure Kubernetes Network Policies.

5. Create policies to govern image provenance using the ImagePolicyWebhook

Prevent unapproved images from being used with the admission controller ImagePolicyWebhook to reject pods that use unapproved images including:

You can learn more about ImagePolicyWebhook here.

6. Securely configure the Kubernetes API server

The Kubernetes API server handles all the REST API calls between external users and Kubernetes components.

Run the below command on your master node:

ps -ef | grep kube-apiserver

In the output, check to ensure that the:

7. Securely configure the kube-scheduler

As the default scheduler for Kubernetes, kube-scheduler selects the node that a newly created Pod should run on. You can learn more about kube-scheduler here.

Run the below command on your master node:

ps -ef | grep kube-scheduler

In the output, check to ensure that the:

8. Securely configure the kube-controller-manager

Run the below command on your master node:

ps -ef | grep kube-controller-manager

In the output, check to ensure that the:

Secure the configuration files on the master node

Secure the API server pod specification file permissions.

Run the following command on the master node (specifying your file location on your system):

stat -c %a /etc/kubernetes/manifests/kube-apiserver.yaml

In the output, check to ensure that permissions are 644 or more restrictive to maintain the integrity of the file.

Secure the API Server pod specification file ownership.

Run the following command on the master node (specifying your file location on your system):

stat -c %U:%G /etc/kubernetes/manifests/kube-apiserver.yaml

In the output, check to ensure that ownership is set as root:root to maintain the integrity of the file.

Secure the controller manager pod specification file permissions.

Run the following command on the master node (specifying your file location on your system):

stat -c %a /etc/kubernetes/manifests/kube-controller-manager.yaml

In the output, check to ensure that permissions are 644 or more restrictive to maintain the integrity of the file.

Secure the controller manager pod specification file ownership.

Run the following command on the master node (specifying your file location on your system):

stat -c %U:%G /etc/kubernetes/manifests/kube-controller-manager.yaml

In the output, check to ensure that ownership is set as root:root to maintain the integrity of the file.

Secure the scheduler pod specification file permissions.

Run the following command on the master node (specifying your file location on your system):

stat -c %a /etc/kubernetes/manifests/kube-scheduler.yaml

In the output, check to ensure that permissions are 644 or more restrictive to maintain the integrity of the file.

Secure the scheduler pod specification file ownership.

Run the following command on the master node (specifying your file location on your system):

stat -c %U:%G /etc/kubernetes/manifests/kube-scheduler.yaml

In the output, check to ensure that ownership is set as root:root to maintain the integrity of the file.

Secure the etcd pod specification file permissions.

Run the following command on the master node (specifying your file location on your system):

stat -c %a /etc/kubernetes/manifests/etcd.yaml

In the output, check to ensure that permissions are 644 or more restrictive to maintain the integrity of the file. As a reminder on a topic already discussed, etcd is a key-value store, and protecting it is of the utmost importance, since it contains your REST API objects.

Secure the etcd pod specification file ownership.

Run the following command on the master node (specifying your file location on your system):

stat -c %U:%G /etc/kubernetes/manifests/etcd.yaml

In the output, check to ensure that ownership is set as root:root to maintain the integrity of the file.

Secure the Container Network Interface file permissions.

Run the following command on the master node (specifying your file location on your system):

stat -c %a <path/to/cni/files>

In the output, check to ensure that permissions are 644 or more restrictive to maintain the integrity of the file.

Secure the Container Network Interface file ownership.

Run the following command on the master node (specifying your file location on your system):

stat -c %U:%G <path/to/cni/files>

In the output, check to ensure that ownership is set as root:root to maintain the integrity of the file.

Secure the etcd data directory permissions.

First run the following command to the get etcd data directory:

ps -ef | grep etcd

Now run the following command based on the etcd data directory you found from the previous command:

stat -c %a /var/lib/etcd

In the output, check to ensure that permissions are 700 or more restrictive to ensure your etcd data directory is protected against unauthorized reads/writes.

Secure the etcd data directory ownership.

First run the following command to the get etcd data directory:

ps -ef | grep etcd

Now run the following command based on the etcd data directory you found from the previous command:

stat -c %U:%G /var/lib/etcd

In the output, check to ensure that ownership is etcd:etcd to ensure your etcd data directory is protected against unauthorized reads/writes.

Secure the admins.conf file permissions.

Run the following command on the master node (specifying your file location on your system):

stat -c %a /etc/kubernetes/admin.conf

In the output, check to ensure that permissions are 644 or more restrictive to maintain the integrity of the file.

Secure the admins.conf file ownership.

Run the following command on the master node (specifying your file location on your system):

stat -c %U:%G /etc/kubernetes/admin.conf

In the output, check to ensure that ownership is set as root:root to maintain the integrity of the file.

Secure the scheduler.conf file permissions.

Run the following command on the master node (specifying your file location on your system):

stat -c %a /etc/kubernetes/scheduler.conf

In the output, check to ensure that permissions are 644 or more restrictive to maintain the integrity of the file.

Secure the scheduler.conf file ownership.

Run the following command on the master node (specifying your file location on your system):

stat -c %U:%G /etc/kubernetes/scheduler.conf

In the output, check to ensure that ownership is set as root:root to maintain the integrity of the file.

Secure the controller-manager.conf file permissions.

Run the following command on the master node (specifying your file location on your system):

stat -c %a /etc/kubernetes/controller-manager.conf

In the output, check to ensure that permissions are 644 or more restrictive to maintain the integrity of the file.

Secure the controller-manager.conf file ownership.

Run the following command on the master node (specifying your file location on your system):

stat -c %U:%G /etc/kubernetes/controller-manager.conf

In the output, check to ensure that ownership is set as root:root to maintain the integrity of the file.

Secure the Kubernetes PKI directory and file ownership.

Run the following command on the master node (specifying your file location on your system):

ls -laR /etc/kubernetes/pki/

In the output, check to ensure that ownership is set as root:root to maintain the integrity of the file.

Secure the Kubernetes PKI directory and file permissions.

Run the following command on the master node (specifying your file location on your system):

ls -laR /etc/kubernetes/pki/*.crt

In the output, check to ensure that permissions are 644 or more restrictive to maintain the integrity of the file.

Secure the Kubernetes PKI key file permissions.

Run the following command on the master node (specifying your file location on your system):

ls -laR /etc/kubernetes/pki/*.key

In the output, check to ensure that permissions are 600 to maintain the integrity of the file.

10. Securely configure etcd

As mentioned in previous sections, etcd (a CNCF project) is a key-value store (a CNCF project) used by distributed systems such as Kubernetes for data access. etcd is considered the source of truth for Kubernetes, and you can read data from and write into etcd as needed. Securely configuring etcd and communications to its servers are of utmost criticality.

Run the following command on the etcd server node:

ps -ef | grep etcd

In the output, check to ensure that the:

11. Securely configure the Kubelet

The kubelet is the main “node agent” running on each node. Misconfiguring kubelet can expose you to a host of security risks, as this Medium article last year outlines. You can either use arguments on the running kubelet executable or a kubelet config file to set the configuration of your kubelet.

To find the kubelet config file, run the following command:

ps -ef | grep kubelet | grep config

Look for --config argument, which will give you the location of the kubelet config file.

Then run the following command on each node:

ps -ef | grep kubelet

In the output, make sure that the:

12. Secure the worker node configuration files

Secure the kubelet service file permissions.

Run the following command on each worker node (specifying your file location on your system):

stat -c %a /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

In the output, check to ensure that permissions are 644 or more restrictive to maintain the integrity of the file.

Secure the kubelet.conf file permissions.

Run the following command on each worker node (specifying your file location on your system):

stat -c %a /etc/kubernetes/kubelet.conf

In the output, check to ensure that permissions are 644 or more restrictive to maintain the integrity of the file.

Secure the kubelet.conf file ownership.

Run the following command on each worker node (specifying your file location on your system):

stat -c %U:%G /etc/kubernetes/kubelet.conf

In the output, check to ensure that ownership is set as root:root to maintain the integrity of the file.

Secure the kublete service file ownership.

Run the following command on each worker node (specifying your file location on your system):

stat -c %U:%G /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

In the output, check to ensure that ownership is set as root:root to maintain the integrity of the file.

Secure the proxy kubeconfig file permissions.

Run the following command to first find the kubeconfig file being used:

ps -ef | grep kube-proxy

Get the kube-proxy file location (if it’s running) from --kubeconfig, then run the following command on each worker node (specifying your file location on your system).

stat -c %a <proxy kubeconfig file>

In the output, check to make sure permissions are 644 or more restrictive to maintain the integrity of the file.

Secure the proxy kubeconfig file ownership.

Run the following command first to find the kubeconfig file being used:

ps -ef | grep kube-proxy

Get the kube-proxy file location (if it’s running) from --kubeconfig, then run the following command on each worker node (specifying your file location on your system):

stat -c %U:%G <proxy kubeconfig file>

In the output, check to make sure ownership is set as root:root to maintain the integrity of the file.

Secure the certificate authorities file permissions.

Run the following command first:

ps -ef | grep kubelet

Look for the file name that’s identified by --client-ca-file argument. Then run the following command, specifying the previous file name:

stat -c %a <filename>

In the output, check to make sure permissions are 644 or more restrictive to maintain the integrity of the file.

Secure the client certificate authorities file ownership.

Run the following command first:

ps -ef | grep kubelet

Look for the file name that’s identified by –-client-ca-file argument. Then run the following command, specifying the previous file name:

stat -c %U:%G <filename>

In the output, check to make sure ownership is set asroot:root to maintain the integrity of the file.

Secure the kubelet configuration file permissions.

First locate the kubelet config file with following command:

ps -ef | grep kubelet | grep config

In the output, you may see the location of the config file if it exists. It would look something like /var/lib/kubelet/configuration.yaml.

Using the location of the file (we’ll use the file location from this previous example), run the following command to identify the file’s permissions:

stat -c %a /var/lib/kubelet/configuration.yaml

In the output, check to make sure permissions are set to 644 or more restrictive to ensure the integrity of the file.

Secure the kubelet configuration file ownership.

Run the following command:

ps -ef | grep kubelet | grep config

In the output, you may see the location of the config file if it exists – it would look something like /var/lib/kubelet/configuration.yaml.

Using the location of the file (we’ll use the file location from this previous example), run the following command to identify the file’s permissions:

stat -c %U:%G /var/lib/kubelet/configuration.yaml

In the output, check to make sure ownership is set to root:root to maintain the integrity of the file.

This cloud-native stack offers compelling capabilities for building the most secure applications we’ve ever created – we just need to make sure we’ve got all the knobs and dials set correctly. Leverage these configurations, code examples, and detailed recommendations to avoid the security risks associated with the most common Kubernetes misconfigurations.