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.
- Privileged
- Baseline
- 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:
- Cluster level
- 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
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.
- Create a Namespace called
testytest
.
kubectl create ns testytest
- 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
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
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
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
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")
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:
- Open Policy Agent (OPA) with Gatekeeper.
- 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.