KCSA — Resource Limits & DoS Prevention

6 minute read

Why Resource Limits Matter for Security

In Kubernetes, any pod that doesn’t have resource limits can consume unlimited CPU, memory, and storage on a node. This creates a serious security risk:

  • A compromised container can starve other workloads of resources
  • A fork bomb can crash an entire node
  • A memory leak can trigger the OOM killer, disrupting co-located pods
  • An attacker can launch a denial-of-service (DoS) attack from within the cluster

Resource limits aren’t just an operational best practice — they’re a security control.


Resource Requests vs Limits

Kubernetes uses two fields to manage container resources:

Field Purpose What Happens
requests Minimum resources guaranteed to the container Used by the scheduler for pod placement
limits Maximum resources the container can use Enforced by the kubelet at runtime

YAML Example

containers:
- name: app
  image: myapp:1.0
  resources:
    requests:
      cpu: "100m"       # Guaranteed 0.1 CPU core
      memory: "128Mi"   # Guaranteed 128 MiB memory
    limits:
      cpu: "500m"       # Cannot exceed 0.5 CPU core
      memory: "256Mi"   # Cannot exceed 256 MiB memory

What Happens When Limits Are Exceeded?

The behavior differs between CPU and memory:

Resource When Limit Is Exceeded Effect
CPU Container is throttled Pod continues running but slower — CPU cycles are restricted
Memory Container is OOMKilled Pod is terminated and restarted (OOM = Out of Memory)

Key Distinction: CPU is a compressible resource (can be throttled). Memory is an incompressible resource (cannot be reclaimed without killing the process).


LimitRange — Default Limits per Namespace

A LimitRange sets default, minimum, and maximum resource constraints for containers in a namespace. It applies automatically to pods that don’t specify their own resource requests/limits.

Key Features

Feature Description
Default values Automatically applied to containers without explicit resources
Min/Max enforcement Rejects pods that request resources outside the range
Per-container Applies to individual containers, not the pod as a whole
Namespace-scoped Each namespace can have its own LimitRange

YAML Example

apiVersion: v1
kind: LimitRange
metadata:
  name: container-limits
  namespace: production
spec:
  limits:
  - type: Container
    default:           # Applied if no limit is specified
      cpu: "500m"
      memory: "256Mi"
    defaultRequest:    # Applied if no request is specified
      cpu: "100m"
      memory: "128Mi"
    max:               # Maximum allowed
      cpu: "2"
      memory: "1Gi"
    min:               # Minimum allowed
      cpu: "50m"
      memory: "64Mi"

With this LimitRange:

  • A container with no resources gets requests: {cpu: 100m, memory: 128Mi} and limits: {cpu: 500m, memory: 256Mi}
  • A container requesting more than max (e.g., 4 CPU) is rejected
  • A container requesting less than min (e.g., 10m CPU) is rejected

LimitRange for Pods

You can also set limits at the pod level:

spec:
  limits:
  - type: Pod
    max:
      cpu: "4"
      memory: "4Gi"

ResourceQuota — Aggregate Limits per Namespace

A ResourceQuota sets total resource limits for an entire namespace. Unlike LimitRange (per-container), ResourceQuota limits the sum of all resources consumed by all pods in the namespace.

YAML Example

apiVersion: v1
kind: ResourceQuota
metadata:
  name: namespace-quota
  namespace: production
spec:
  hard:
    requests.cpu: "10"           # Total CPU requests across all pods
    requests.memory: "20Gi"      # Total memory requests across all pods
    limits.cpu: "20"             # Total CPU limits across all pods
    limits.memory: "40Gi"        # Total memory limits across all pods
    pods: "50"                   # Maximum 50 pods in this namespace
    services: "10"               # Maximum 10 services
    persistentvolumeclaims: "20" # Maximum 20 PVCs
    configmaps: "50"             # Maximum 50 ConfigMaps
    secrets: "100"               # Maximum 100 Secrets

Key Points

  • When a ResourceQuota is active, all pods must specify resource requests and limits (or have them applied via LimitRange). Otherwise, the pod is rejected.
  • ResourceQuota prevents a single namespace from monopolizing cluster resources
  • Especially important in multi-tenant environments

LimitRange vs ResourceQuota

Aspect LimitRange ResourceQuota
Scope Per-container/pod Per-namespace (aggregate)
Purpose Set defaults and min/max per container Limit total resources for the namespace
When applied At container creation time Continuously tracked
What it controls Individual container resources Sum of all resources in namespace
Use together? ✅ Yes — complementary controls ✅ Yes — complementary controls

Best Practice: Use both together. LimitRange sets sane defaults per container. ResourceQuota caps the total for the namespace.


Quality of Service (QoS) Classes

Kubernetes assigns a QoS class to each pod based on its resource configuration. The QoS class determines which pods are evicted first when a node runs out of resources.

QoS Class Condition Eviction Priority
Guaranteed Every container has requests == limits for both CPU and memory Last to evict (highest priority)
Burstable At least one container has requests < limits, or only requests set Middle priority
BestEffort No containers have any requests or limits set First to evict (lowest priority)

Examples

Guaranteed:

resources:
  requests:
    cpu: "500m"
    memory: "256Mi"
  limits:
    cpu: "500m"       # Same as requests
    memory: "256Mi"   # Same as requests

Burstable:

resources:
  requests:
    cpu: "100m"
    memory: "128Mi"
  limits:
    cpu: "500m"       # Different from requests
    memory: "256Mi"   # Different from requests

BestEffort:

# No resources section at all
containers:
- name: app
  image: myapp:1.0
  # No resources specified

Security Implication: BestEffort pods are evicted first but can consume unlimited resources until eviction. In a security context, always ensure at least Burstable QoS (set requests) to avoid unpredictable resource use.


DoS Attack Vectors in Kubernetes

Attackers — or even buggy workloads — can cause denial of service through several vectors:

Attack Vector Description Impact
Fork bomb Process creates unlimited child processes Node CPU and memory exhaustion
Memory leak Container allocates memory without releasing it OOM kills on the node
CPU spin Infinite loop consuming all CPU Other pods starved of CPU
Pod flooding Creating thousands of pods via compromised service account API server overload, node exhaustion
Storage exhaustion Writing unlimited data to volumes or container filesystem Node disk full, all pods affected
Network flooding Generating massive network traffic Network saturation, pod unreachability
Log bombing Writing excessive logs to stdout/stderr Disk exhaustion on the node

Prevention Strategies

Attack Vector Prevention Kubernetes Resource
Fork bomb Set PID limits per pod kubelet --pod-max-pids or PodPidsLimit feature
Memory leak Set memory limits resources.limits.memory
CPU spin Set CPU limits resources.limits.cpu
Pod flooding Limit pod count per namespace ResourceQuota (pods: N)
Storage exhaustion Set ephemeral storage limits resources.limits.ephemeral-storage, emptyDir.sizeLimit
Network flooding Apply network policies + rate limits NetworkPolicy + CNI features
Log bombing Set container log rotation on kubelet --container-log-max-size, --container-log-max-files

Ephemeral Storage Limits

Containers can write to the node’s local filesystem (container writable layer, logs, emptyDir). Limit this with:

resources:
  requests:
    ephemeral-storage: "1Gi"
  limits:
    ephemeral-storage: "2Gi"

PID Limits

To prevent fork bombs, configure the kubelet to limit PIDs per pod:

# kubelet configuration
podPidsLimit: 1024    # Maximum 1024 processes per pod

Comprehensive Security Example

A namespace with full resource controls:

# Step 1: LimitRange — defaults for every container
apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: production
spec:
  limits:
  - type: Container
    default:
      cpu: "500m"
      memory: "256Mi"
      ephemeral-storage: "1Gi"
    defaultRequest:
      cpu: "100m"
      memory: "128Mi"
      ephemeral-storage: "500Mi"
    max:
      cpu: "2"
      memory: "2Gi"
    min:
      cpu: "50m"
      memory: "64Mi"
---
# Step 2: ResourceQuota — cap total namespace usage
apiVersion: v1
kind: ResourceQuota
metadata:
  name: namespace-quota
  namespace: production
spec:
  hard:
    requests.cpu: "20"
    requests.memory: "40Gi"
    limits.cpu: "40"
    limits.memory: "80Gi"
    pods: "100"
    services: "20"
    persistentvolumeclaims: "50"

Key Takeaways for the Exam

  1. Requests = guaranteed minimum (used for scheduling). Limits = enforced maximum (throttle or kill).

  2. CPU is compressible (throttled when limit exceeded). Memory is incompressible (OOMKilled when limit exceeded).

  3. LimitRange sets per-container defaults, min, and max. ResourceQuota sets per-namespace aggregate limits.

  4. QoS classes: Guaranteed (requests == limits, last evicted), Burstable (requests < limits, middle), BestEffort (no requests/limits, first evicted).

  5. Always use resource limits — pods without limits can consume unlimited resources, enabling DoS attacks.

  6. Use ResourceQuota with pods: N to prevent pod flooding attacks.

  7. LimitRange and ResourceQuota are complementary — use both for comprehensive resource control.

  8. When ResourceQuota is active, all pods must specify resources (manually or via LimitRange defaults).


Next: Practice Questions — test your knowledge of pod and network security concepts.

Updated: