Implementing Kubernetes Pod Security Standards

Michael Levan - Jul 29 - - Dev Community

Just about every security issue is due to a misconfiguration.

Think about it - security bugs are always due to some misconfiguration at the code level. Breaches are always due to some bad authentication/authorization protocol, a bug, or some vulnerability (and the vulnerability is due to a misconfiguration as well).

With Kubernetes, it’s no different, except you now have to worry about bugs at both the cluster and the application (Pod/container) level.

In this blog post, you’ll learn about one of the many ways to secure Pods with Pod Security Standards.

What Are Pod Security Standards

Pod Security Standards (PSS) give you three fairly broad policy enforcement capabilities.

  1. Privileged
  2. Baseline
  3. Restricted

Privileged is an “open to all” policy. It allows for known privilege execution and is the “least secure” of them all. Just because it’s open, however, doesn’t mean that the policy is inherently bad. You’ll primarily see this policy on system and infrastructure-based workloads that are performed by trusted engineers (typically the Kubernetes admins). This is very much a “allow all by default” policy, so if you’re using it, you want to ensure to be careful.

Restricted is targeted to ensure the enforcement of Pod hardening best practices. As with most hardened systems, there are some compatibility issues as they typically lock close to everything down by default. Think about it from a Defense In Depth perspective. The default thought process of the Restricted policy is “trust no one”. Depending on your organizations security policies, you may end up wanting to start with this policy and opening it up as you go. It’s always easier to give more capabilities than it is to take away.

Baseline is a healthy combination of both and it’s what you’ll usually see within Kubernetes environments. You’ll primarily see this policy used for ease of adoption. It does, however, prevent known privilege escalation.

Pod Security Standards vs Pod Security Admission

Pod Security Admissions and Standards work together. Once Pod Security Standards are configured, Pod Security Admission enforces the policies via the Admission Controller.

The Pod Security Admission Controller

By default, the Pod Security Admission controller is enabled for Kubernetes API v1.23 and above. At the time of writing this, the Kubernetes API is currently on v1.30 with v1.31 in the works. If you aren’t already above Kubernetes API v1.23, it’s highly suggested that you upgrade from both a security and deprecation perspective.

If you need to upgrade your Kubernetes cluster, the best thing that you can do is go to the documentation from your provider (Kubeadm, AKS, EKS, etc.) and go through the steps that they provide.

Policies At The Cluster Level

There are two ways to implement Pod Security Standards:

  1. Cluster level
  2. Namespace level

At the cluster level, just remember that as the name suggests, the impact is throughout the entire cluster. There is however an exceptions flag as you can see below.

The Cluster Level configuration comes from the apiserver.config CRD and is implemented with the AdmissionConfiguration object/resource.

You can run the below to implement Pod Security Standards across the cluster.

kubectl apply -f - <<EOF
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
  configuration:
    apiVersion: pod-security.admission.config.k8s.io/v1
    kind: PodSecurityConfiguration
    defaults:
      enforce: "baseline"
      enforce-version: "latest"
      audit: "restricted"
      audit-version: "latest"
      warn: "restricted"
      warn-version: "latest"
    exemptions:
      usernames: []
      runtimeClasses: []
      namespaces: [kube-system]
EOF
Enter fullscreen mode Exit fullscreen mode

Please note that based on where your Kubernetes cluster is running, AdmissionConfiguration may not be available. For example, at the time of writing this, AdmissionConfiguration isn’t available on Azure Kubernetes Service (AKS) and most likely other cloud providers.

Policies Per Namespace

By default, the following Namespaces are always set to the Privileged:

  • default
  • kube-public
  • kube-system

If you want to update, change, or create a new Policy on one or all Namespaces, you can. By specifying the ns flag, you can choose whether you want to target a specific Namespace or all Namespaces.

  1. Create a Namespace called testytest.
kubectl create ns testytest
Enter fullscreen mode Exit fullscreen mode
  1. Label the Namespace with the baseline Pod Security Standard.
kubectl label --overwrite namespace testytest\
  pod-security.kubernetes.io/audit=baseline\
  pod-security.kubernetes.io/warn=baseline
Enter fullscreen mode Exit fullscreen mode

Using the --all flag, you can set the Pod Security Standard: Baseline across all Namespaces.

kubectl label --overwrite namespace --all \
  pod-security.kubernetes.io/audit=baseline \
  pod-security.kubernetes.io/warn=baseline
Enter fullscreen mode Exit fullscreen mode

Testing Policies

When implementing Pod Security Standards, there are certain policies that application stacks being deployed via Kubernetes must follow. For example, if you set the Pod Security Standard to restricted, that means allowPrivilegeEscalation in the SecurityContext must be set to false.

Let’s test out this theory.

Label the testytest Namespace with the Restricted Pod Security Standard.

kubectl label --overwrite namespace testytest\
  pod-security.kubernetes.io/audit=restricted\
  pod-security.kubernetes.io/warn=restricted
Enter fullscreen mode Exit fullscreen mode

Run the following Kubernetes Deployment which sets allowPrivilegeEscalation to true instead of false.

kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: testytest
spec:
  selector:
    matchLabels:
      app: nginxdeployment
  replicas: 2
  template:
    metadata:
      namespace: webapp
      labels:
        app: nginxdeployment
    spec:
      containers:
      - name: nginxdeployment
        image: nginx:latest
        securityContext:
          allowPrivilegeEscalation: true
          readOnlyRootFilesystem: false
          privileged: true
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
          limits:
            memory: "128Mi"
        ports:
        - containerPort: 80
EOF
Enter fullscreen mode Exit fullscreen mode

After running the above, you’ll see an output similar to the below which indicates rules that are being broken per the Restricted standard.

Warning: would violate PodSecurity "restricted:latest": privileged (container "nginxdeployment" must not set securityContext.privileged=true), allowPrivilegeEscalation != false (container "nginxdeployment" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "nginxdeployment" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "nginxdeployment" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "nginxdeployment" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
Enter fullscreen mode Exit fullscreen mode

How About At The Pod Manifest Level?

If you’re wondering “How about setting these standards at the Pod level?”, you cannot currently do it with Pod Security Standards. You’ll need to use a Policy Enforcer to set policies at the Pod level.

Two of the most popular Policy Enforcement tools for Kubernetes right now are:

  1. Open Policy Agent (OPA) with Gatekeeper.
  2. Kyverno.

Kyverno recently went from just working on Kubernetes to working on outside platforms.

Open Policy Agent (OPA) is a policy enforcement tool that works across all different types of systems. For OPA to work with Kubernetes, it uses something called Gatekeeper. Gatekeeper is like the “shim” between OPA and Kubernetes. Kubernetes doesn’t know how to speak OPA and OPA doesn’t know how to speak Kubernetes, so it uses Gatekeeper.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .