Kubernetes has become the industry-standard container orchestrator for companies running complex distributed systems. However, because it is a robust and flexible ecosystem, its default configuration prioritizes ease of development over restrictive security.
Without proper hardening, attackers can escalate privileges from a single container to compromise the entire cluster. Below, we highlight essential practices to secure your Kubernetes cluster.
1. Implement the Principle of Least Privilege with RBAC
Role-Based Access Control (RBAC) allows you to restrict which actions users and pods can perform on Kubernetes APIs.
Recommendations:
- Never use the default
system:serviceaccount:defaultaccount with administrator privileges. - Create dedicated
ServiceAccountsfor each application and associate only the necessary permissions (e.g., read-only in the corresponding namespace).
2. Restrict Access to Pod Resources (Security Context)
By default, Docker containers can run as the root user. If a root container is compromised due to vulnerabilities, the attacker gains full access to the host physical node.
What to configure in manifests (YAML):
Configure your Pod's securityContext block to prevent privileged execution:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
containers:
- name: my-app
image: my-app:latest
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
Note: Using readOnlyRootFilesystem blocks any writes to the container's local disk, forcing the use of temporary directories (tmpfs) or persistent volumes explicitly mounted for writing, which prevents the injection of malicious scripts.
3. Apply Network Policies (Network Isolation)
By default, the Kubernetes internal network is open: any Pod from any Namespace can communicate freely with other Pods in the cluster.
How to protect:
- Enable network policies (NetworkPolicies) to act as an internal layer 3/4 firewall.
- Isolate databases and queue systems, configuring them to accept connections only from pods marked with the specific label of the authorized microservice.
4. RBAC and a NetworkPolicy in the namespace
The pod account does not inherit cluster permission. This role only lists pods in the shop namespace:
apiVersion: v1
kind: ServiceAccount
metadata:
name: shop-api
namespace: shop
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: read-pods
namespace: shop
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: shop-api-read-pods
namespace: shop
subjects:
- kind: ServiceAccount
name: shop-api
namespace: shop
roleRef:
kind: Role
name: read-pods
apiGroup: rbac.authorization.k8s.io
The network closes the database. Only the pod labeled app=shop-api may talk to port 5432:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: postgres-from-api
namespace: shop
spec:
podSelector:
matchLabels:
app: postgres
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels:
app: shop-api
ports:
- protocol: TCP
port: 5432
kubectl apply -f rbac-shop.yaml
kubectl apply -f netpol-postgres.yaml
kubectl auth can-i list pods --as=system:serviceaccount:shop:shop-api -n shop
kubectl auth can-i delete pods --as=system:serviceaccount:shop:shop-api -n shop
The first answer is yes. The second is no. A NetworkPolicy filters traffic only when the cluster CNI implements the API. Without that, the YAML applies and the network stays open.
Conclusion
Securing Kubernetes clusters requires a layered approach. By applying network isolation, strict RBAC policies, and security contexts to Pods, your organization eliminates the main blind spots of containerized infrastructures in production.