Securing Applications in Kubernetes

DevOps and Cloud Engineer
Focused on optimizing the software development lifecycle through seamless integration of development and operations, specializing in designing, implementing, and managing scalable cloud infrastructure with a strong emphasis on automation and collaboration.
Key Skills:
Terraform: Skilled in Infrastructure as Code (IaC) for automating infrastructure deployment and management. Ansible: Proficient in automation tasks, configuration management, and application deployment. AWS: Extensive experience with AWS services like EC2, S3, RDS, and Lambda, designing scalable and cost-effective solutions. Kubernetes: Expert in container orchestration, deploying, scaling, and managing containerized applications. Docker: Proficient in containerization for consistent development, testing, and deployment. Google Cloud Platform: Familiar with GCP services for compute, storage, and machine learning.
Introduction
Providing a secure platform to run workloads is critical for Kubernetes adoption in production environments. While Kubernetes offers numerous security-focused APIs, implementing them effectively requires understanding core principles like least privilege and defense-in-depth.
The principle of least privilege means provisioning only the resources needed for Kubernetes workloads—nothing more. This guide walks through implementing comprehensive security controls across every layer of your Kubernetes infrastructure.
Understanding Security Context: The Foundation
At the core of securing pods is the Security Context—an aggregation of all security-focused fields that can be applied at both pod and container specification levels.
Core Security Controls
Security Context covers:
User permissions and access control
Read-only root filesystem
Privilege escalation controls
Seccomp, AppArmor, and SELinux profiles
Privileged vs unprivileged execution
Practical Implementation
Here's a comprehensive example of a secured pod:
apiVersion: v1
kind: Pod
metadata:
name: kuard
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
containers:
- image: gcr.io/kuar-demo/kuard-amd64:blue
name: kuard
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
privileged: false
ports:
- containerPort: 8080
name: http
protocol: TCP
Security Context Field Breakdown
runAsNonRoot: true Ensures the container process doesn't run as root (UID 0). Kubernetes will reject containers that attempt to run as root, significantly reducing the attack surface.
runAsUser: 1000 / runAsGroup: 3000 Explicitly sets user and group IDs instead of relying on container image defaults. This provides predictable, controlled permissions.
fsGroup: 2000 Sets group ownership for mounted volumes. When volumes are mounted, their group ownership changes to 2000 with group write permissions.
allowPrivilegeEscalation: false Prevents processes from gaining more privileges than their parent process, blocking operations like sudo or setuid binaries.
privileged: false Ensures containers run in unprivileged mode, maintaining proper container isolation from the host system.
readOnlyRootFilesystem: true Mounts the container's root filesystem as read-only, preventing malware from modifying system files.
Verification: Testing Your Security Controls
Let's verify these settings work as expected:
# Start a shell in the secured pod
kubectl exec -it kuard -- ash
# Check user and group IDs
id
# Output: uid=1000 gid=3000 groups=2000
# Verify processes run as non-root
ps aux
# All processes show user 1000, not root
# Test read-only filesystem
touch /test-file
# Output: touch: /test-file: Read-only file system
This confirms our security context is properly enforced.
Advanced Security Controls
Enhanced Pod with Multiple Security Layers
apiVersion: v1
kind: Pod
metadata:
name: amicontained
annotations:
container.apparmor.security.beta.kubernetes.io/amicontained: "runtime/default"
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
containers:
- image: r.j3ss.co/amicontained:v0.4.9
name: amicontained
command: ["/bin/sh", "-c", "--"]
args: ["amicontained"]
securityContext:
capabilities:
add: ["SYS_TIME"]
drop: ["NET_BIND_SERVICE"]
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
privileged: false
Security Enhancement Breakdown
AppArmor Integration
annotations:
container.apparmor.security.beta.kubernetes.io/amicontained: "runtime/default"
Applies runtime default AppArmor profile for additional access control.
Seccomp Profile
seccompProfile:
type: RuntimeDefault
Uses runtime's default seccomp profile, blocking 21+ dangerous system calls.
Linux Capabilities
capabilities:
add: ["SYS_TIME"] # Grant specific capabilities
drop: ["NET_BIND_SERVICE"] # Remove dangerous capabilities
Pod Security Standards
Kubernetes provides three built-in security levels for systematic policy enforcement:
Security Levels
Baseline 🟡
Prevents most privilege escalations
Good starting point for most workloads
Enables easier onboarding
Restricted 🔴
Highly restrictive security best practices
May cause some workloads to break
Recommended for high-security environments
Privileged 🟢
Open and unrestricted
Allows dangerous configurations
Use only when absolutely necessary
Implementing Pod Security Standards
apiVersion: v1
kind: Namespace
metadata:
name: baseline-ns
labels:
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/enforce-version: v1.22
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/audit-version: v1.22
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: v1.22
Enforcement Modes
Enforce: Pods violating the policy are denied
Warn: Violations show warning messages but allow pods
Audit: Violations generate audit log entries
Service Accounts and RBAC
Limit API access by controlling service account token mounting:
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
automountServiceAccountToken: false
Why disable token mounting?
Service account tokens provide cluster API access
Many applications don't need this access
Follows principle of least privilege
Removes potential attack vector
RuntimeClass: Enhanced Isolation
Select different container runtimes with varying security isolation levels:
apiVersion: v1
kind: Pod
metadata:
name: kuard
labels:
app: kuard
spec:
runtimeClassName: firecracker
containers:
- image: gcr.io/kuar-demo/kuard-amd64:blue
name: kuard
ports:
- containerPort: 8080
name: http
protocol: TCP
Runtime Options
runc: Standard OCI runtime (default)
kata: VM-based containers (stronger isolation)
firecracker: Lightweight VMs (AWS Lambda technology)
gVisor: User-space kernel (Google's container sandbox)
Network Policy: Traffic Control
Implement "default deny, explicit allow" network security:
Default Deny Policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
Selective Allow Policy
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: access-kuard
spec:
podSelector:
matchLabels:
app: kuard
ingress:
- from:
- podSelector:
matchLabels:
run: test-source
Network Policy Key Concepts
Additive nature: Multiple policies combine (logical OR)
Explicit definition required: Once matched by NetworkPolicy, all traffic must be explicitly allowed
Controller dependency: Requires CNI support (Calico, Cilium, Weave Net)
Service Mesh Security
Add application-layer security with service mesh:
Key Benefits:
Protocol-aware policies (HTTP methods, paths, headers)
Mutual TLS (mTLS) for automatic encryption
Service identity verification
Fine-grained access control
# Example Istio AuthorizationPolicy
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: allow-serviceA-to-serviceB
spec:
selector:
matchLabels:
app: serviceB
rules:
- from:
- source:
principals: ["cluster.local/ns/default/sa/serviceA"]
to:
- operation:
methods: ["GET", "POST"]
paths: ["/api/*"]
Security Benchmarking with kube-bench
Regular security assessment using CIS Kubernetes Benchmarks:
apiVersion: batch/v1
kind: Job
metadata:
name: kube-bench
spec:
template:
spec:
hostPID: true
nodeSelector:
kubernetes.io/os: linux
tolerations:
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
containers:
- name: kube-bench
image: aquasec/kube-bench:latest
command: ["kube-bench"]
volumeMounts:
- name: var-lib-etcd
mountPath: /var/lib/etcd
readOnly: true
- name: var-lib-kubelet
mountPath: /var/lib/kubelet
readOnly: true
- name: etc-systemd
mountPath: /etc/systemd
readOnly: true
- name: etc-kubernetes
mountPath: /etc/kubernetes
readOnly: true
- name: usr-bin
mountPath: /usr/local/mount-from-host/bin
readOnly: true
restartPolicy: Never
volumes:
- name: var-lib-etcd
hostPath:
path: "/var/lib/etcd"
- name: var-lib-kubelet
hostPath:
path: "/var/lib/kubelet"
- name: etc-systemd
hostPath:
path: "/etc/systemd"
- name: etc-kubernetes
hostPath:
path: "/etc/kubernetes"
- name: usr-bin
hostPath:
path: "/usr/bin"
Understanding kube-bench Output
[PASS]: Compliant configurations
[FAIL]: Security gaps requiring attention
[WARN]: Potential issues to investigate
Image Security
Container security extends beyond runtime controls:
Secure Development Practices
Keep code and applications within pods secure
Use minimal base images
Regular vulnerability scanning
Registry Security
Scan images for known vulnerabilities
Use trusted registries
Implement image signing and verification
Runtime Security
Continuous monitoring for malicious activity
Detect intrusions and policy violations
Rapid patch deployment for vulnerabilities
Complete Security Strategy
Defense-in-Depth Layers
Pod Security Context - User permissions, filesystem access, capabilities
Pod Security Standards - Baseline, restricted, and privileged policies
AppArmor/SELinux/Seccomp - Kernel-level access controls
RBAC & Service Accounts - API access limitation
RuntimeClass - Container isolation levels
Network Policy - Traffic control and segmentation
Service Mesh - Application-layer security and mTLS
Image Security - Vulnerability management and scanning
Security Benchmarks - Compliance assessment and remediation
Implementation Workflow
Start with defaults - Apply Pod Security Standards
Layer additional controls - Add NetworkPolicy, SecurityContext
Assess compliance - Run kube-bench regularly
Monitor continuously - Use security scanning and monitoring
Iterate and improve - Regular security reviews and updates
Key Security Principles
Least Privilege - Minimal permissions and capabilities
Defense-in-Depth - Multiple overlapping security layers
Default Deny - Block by default, allow by exception
Continuous Monitoring - Ongoing security assessment
Rapid Response - Quick patching and remediation



