Skip to content

Module 4.6: KubeArmor - Runtime Security with Least Privilege

Time to Complete: 90 minutes Prerequisites: Module 4.3 (Falco), Module 4.5 (Tetragon basics), Understanding of Linux security (AppArmor/SELinux) Learning Objectives:

  • Understand runtime security enforcement with KubeArmor
  • Deploy KubeArmor for workload protection
  • Create KubeArmorPolicy for allow-listing and blocking
  • Implement least-privilege security at the container level

After completing this module, you will be able to:

  • Deploy KubeArmor for container-aware runtime security with LSM-based policy enforcement
  • Configure KubeArmor security policies for process, file, and network access control per workload
  • Implement KubeArmor’s visibility mode to discover application behavior before enforcing restrictions
  • Compare KubeArmor’s LSM approach against Tetragon’s eBPF enforcement for different kernel requirements

Container security defaults are permissive. A container can typically execute any binary, access any file it has permissions for, and make any network connection. This is the “default allow” model—everything is permitted unless explicitly blocked.

KubeArmor flips this to “default deny” for containers.

Instead of listing what to block (and missing something), you define what’s allowed. Everything else is automatically denied. This is least privilege enforcement—containers can only do what they need, nothing more.

“Stop playing whack-a-mole with threats. Define what’s legitimate, block everything else.”


  • KubeArmor supports three enforcement backends: AppArmor, BPF-LSM, and SELinux—choosing the best available on each node
  • KubeArmor can auto-generate policies by observing your application’s behavior in learning mode
  • You can get immediate visibility into what your containers are actually doing before enforcing anything
  • KubeArmor is CNCF Sandbox project with roots in AccuKnox’s enterprise security platform
  • The same policies work across VMs, containers, and bare metal with the KubeArmor host protection agent
  • KubeArmor integrates with Kubernetes admission control to prevent insecure workloads from starting

┌─────────────────────────────────────────────────────────────────────────┐
│ Traditional "Block List" Model │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Everything Allowed │ │
│ │ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ │ │
│ │ │ bash │ │ curl │ │ wget │ │ python│ │ ??? │ │ │
│ │ │ ✓ │ │ ✓ │ │ ✓ │ │ ✓ │ │ ✓ │ │ │
│ │ └───────┘ └───────┘ └───────┘ └───────┘ └───────┘ │ │
│ │ │ │
│ │ Block: xmrig, nc, ncat, ... │ │
│ │ (always one step behind attackers) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Problem: What about the tools you haven't heard of yet? │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ KubeArmor "Allow List" Model │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Everything Blocked │ │
│ │ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ │ │
│ │ │ bash │ │ curl │ │ wget │ │ python│ │ ??? │ │ │
│ │ │ ✗ │ │ ✗ │ │ ✗ │ │ ✗ │ │ ✗ │ │ │
│ │ └───────┘ └───────┘ └───────┘ └───────┘ └───────┘ │ │
│ │ │ │
│ │ Allow: /app/server, /usr/bin/node, /lib/*.so │ │
│ │ (only what's needed) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Solution: Unknown tools are blocked by default. │
└─────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ KubeArmor Components │ │
│ │ │ │
│ │ ┌────────────────┐ ┌─────────────────────────────────────────┐ │ │
│ │ │ KubeArmor │ │ KubeArmorPolicy CRDs │ │ │
│ │ │ Operator │ │ - Workload policies │ │ │
│ │ └────────────────┘ │ - Host policies │ │ │
│ │ │ - Cluster policies │ │ │
│ │ └─────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ Node 1 │ │ Node 2 │ │ Node 3 │ │
│ │ ┌─────────┐ │ │ ┌─────────┐ │ │ ┌─────────┐ │ │
│ │ │KubeArmor│ │ │ │KubeArmor│ │ │ │KubeArmor│ │ │
│ │ │ Agent │ │ │ │ Agent │ │ │ │ Agent │ │ │
│ │ │(DaemonSet│ │ │ │(DaemonSet│ │ │ │(DaemonSet│ │ │
│ │ └────┬────┘ │ │ └────┬────┘ │ │ └────┬────┘ │ │
│ │ │ │ │ │ │ │ │ │ │
│ │ ┌────┴────┐ │ │ ┌────┴────┐ │ │ ┌────┴────┐ │ │
│ │ │Enforcer │ │ │ │Enforcer │ │ │ │Enforcer │ │ │
│ │ │(AppArmor│ │ │ │(BPF-LSM)│ │ │ │(SELinux)│ │ │
│ │ │ or │ │ │ │ │ │ │ │ │ │ │
│ │ │BPF-LSM) │ │ │ │ │ │ │ │ │ │ │
│ │ └─────────┘ │ │ └─────────┘ │ │ └─────────┘ │ │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
ComponentRoleDescription
KubeArmor AgentPolicy enforcementDaemonSet on each node, enforces policies
KubeArmor RelayLog aggregationCollects alerts from all agents
kArmor CLIManagementCLI tool for policy management
KubeArmorPolicyWorkload policyPod-level security rules
KubeArmorHostPolicyHost policyNode-level security rules
KubeArmorClusterPolicyCluster policyCluster-wide security rules
BackendKernel VersionDistribution Support
AppArmor2.6+Ubuntu, Debian, SUSE
BPF-LSM5.7+Any with BTF support
SELinux2.6+RHEL, CentOS, Fedora

KubeArmor automatically detects and uses the best available enforcer.


Terminal window
# Add KubeArmor Helm repo
helm repo add kubearmor https://kubearmor.github.io/charts
helm repo update
# Install KubeArmor
helm install kubearmor kubearmor/kubearmor -n kubearmor --create-namespace
# Verify installation
kubectl get pods -n kubearmor
Terminal window
# Install kArmor CLI
curl -sfL http://get.kubearmor.io/ | sudo sh -s -- -b /usr/local/bin
# Install KubeArmor using CLI
karmor install
# Check status
karmor probe
Terminal window
kubectl apply -f https://raw.githubusercontent.com/kubearmor/KubeArmor/main/deployments/get/kubearmor.yaml
Terminal window
# Check KubeArmor pods
kubectl get pods -n kubearmor
# Check if enforcement is working
karmor probe
# Expected output:
# Found node with mass_security: apparmor
# KubeArmor is running
# Active enforcers: [AppArmor]

apiVersion: security.kubearmor.com/v1
kind: KubeArmorPolicy
metadata:
name: example-policy
namespace: default
spec:
selector:
matchLabels:
app: myapp # Which pods this applies to
process:
matchPaths:
- path: /bin/bash
action: Block # Block bash execution
file:
matchDirectories:
- dir: /etc/
readOnly: true # /etc is read-only
network:
matchProtocols:
- protocol: raw
action: Block # Block raw sockets
KubeArmorPolicy
├── selector # Which pods to target
│ └── matchLabels
├── process # Process execution rules
│ ├── matchPaths[] # Specific binaries
│ ├── matchPatterns[] # Pattern matching
│ └── matchDirectories[] # Directory-based
├── file # File access rules
│ ├── matchPaths[]
│ ├── matchPatterns[]
│ └── matchDirectories[]
├── network # Network access rules
│ └── matchProtocols[]
├── capabilities # Linux capability rules
│ └── matchCapabilities[]
└── action # Default: Audit | Block | Allow
ActionEffectUse Case
AllowExplicitly permit (in default-deny mode)Whitelist specific operations
BlockDeny and log the attemptBlock known-bad operations
AuditAllow but logLearning mode, observation

apiVersion: security.kubearmor.com/v1
kind: KubeArmorPolicy
metadata:
name: nodejs-minimal
namespace: production
spec:
selector:
matchLabels:
app: nodejs-api
# Only allow node and necessary system binaries
process:
matchPaths:
- path: /usr/local/bin/node
action: Allow
- path: /usr/bin/node
action: Allow
matchDirectories:
- dir: /
recursive: true
action: Block # Block all other executables
# Only allow reading app files and node_modules
file:
matchDirectories:
- dir: /app/
recursive: true
readOnly: true
action: Allow
- dir: /usr/local/lib/node_modules/
recursive: true
readOnly: true
action: Allow
- dir: /tmp/
action: Allow # Allow tmp for scratch space
matchPaths:
- path: /etc/passwd
readOnly: true
action: Allow # Node needs this for user lookup
- path: /etc/hosts
readOnly: true
action: Allow
# Only allow TCP connections
network:
matchProtocols:
- protocol: tcp
action: Allow
- protocol: udp
action: Allow # DNS
- protocol: raw
action: Block # No raw sockets
apiVersion: security.kubearmor.com/v1
kind: KubeArmorPolicy
metadata:
name: postgres-hardened
namespace: database
spec:
selector:
matchLabels:
app: postgres
process:
matchPaths:
- path: /usr/lib/postgresql/*/bin/postgres
action: Allow
- path: /usr/lib/postgresql/*/bin/pg_ctl
action: Allow
- path: /usr/lib/postgresql/*/bin/initdb
action: Allow
matchDirectories:
- dir: /
recursive: true
action: Block # Only postgres binaries allowed
file:
matchDirectories:
- dir: /var/lib/postgresql/
recursive: true
action: Allow # Data directory
- dir: /var/run/postgresql/
action: Allow # Socket directory
- dir: /etc/postgresql/
recursive: true
readOnly: true
action: Allow # Config (read-only)
capabilities:
matchCapabilities:
- capability: net_bind_service
action: Block # Don't allow binding to privileged ports
apiVersion: security.kubearmor.com/v1
kind: KubeArmorClusterPolicy
metadata:
name: block-shells-cluster-wide
spec:
selector:
matchExpressions:
- key: app
operator: Exists # Applies to all pods with an 'app' label
process:
matchPaths:
- path: /bin/bash
action: Block
- path: /bin/sh
action: Block
- path: /bin/dash
action: Block
- path: /bin/zsh
action: Block
- path: /usr/bin/bash
action: Block
- path: /usr/bin/sh
action: Block
matchPatterns:
- pattern: "**/python*"
action: Block
- pattern: "**/perl*"
action: Block
- pattern: "**/ruby*"
action: Block

4. Protect Kubernetes Service Account Tokens

Section titled “4. Protect Kubernetes Service Account Tokens”
apiVersion: security.kubearmor.com/v1
kind: KubeArmorClusterPolicy
metadata:
name: protect-service-account-tokens
spec:
selector:
matchExpressions:
- key: app
operator: Exists
file:
matchDirectories:
- dir: /var/run/secrets/kubernetes.io/serviceaccount/
recursive: true
action: Block # Block all access to SA tokens
apiVersion: security.kubearmor.com/v1
kind: KubeArmorPolicy
metadata:
name: frontend-network-policy
namespace: production
spec:
selector:
matchLabels:
tier: frontend
network:
matchProtocols:
- protocol: tcp
action: Allow
- protocol: udp
action: Allow # DNS lookups
- protocol: icmp
action: Block # No ping
- protocol: raw
action: Block # No raw sockets
# Note: KubeArmor network policies complement NetworkPolicies
# They enforce at the container level, not pod level

KubeArmor can observe your application and suggest policies:

Terminal window
# Start observing a deployment
karmor discover --namespace production --deployment api-server --output api-policy.yaml
# This watches the application and generates a policy based on:
# - What processes are executed
# - What files are accessed
# - What network connections are made
# Review and apply the generated policy
cat api-policy.yaml
kubectl apply -f api-policy.yaml
┌────────────────────────────────────────────────────────────────────────┐
│ Policy Discovery Flow │
│ │
│ ┌──────────────────┐ │
│ │ 1. Start │ │
│ │ Discovery │ │
│ └────────┬─────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ ┌──────────────────────────────────────┐ │
│ │ 2. KubeArmor │ │ Application runs normally: │ │
│ │ observes all │◀────│ - Executes /usr/bin/node │ │
│ │ activity │ │ - Reads /app/config.json │ │
│ └────────┬─────────┘ │ - Connects to postgres:5432 │ │
│ │ │ - Writes to /tmp/cache │ │
│ │ └──────────────────────────────────────┘ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ 3. Generate │ │
│ │ allowlist │ │
│ │ policy │ │
│ └────────┬─────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 4. Output: KubeArmorPolicy YAML │ │
│ │ │ │
│ │ process: │ │
│ │ matchPaths: │ │
│ │ - path: /usr/bin/node │ │
│ │ action: Allow │ │
│ │ file: │ │
│ │ matchDirectories: │ │
│ │ - dir: /app/ │ │
│ │ action: Allow │ │
│ │ ... │ │
│ └──────────────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────────────┘

Terminal window
# Stream all KubeArmor logs
karmor logs
# Filter by namespace
karmor logs --namespace production
# JSON output for processing
karmor logs --json | jq '.Result'
# Filter by operation type
karmor logs --logFilter=policy # Only policy violations
Event TypeDescription
MatchedPolicyAction taken due to a policy match
MatchedHostPolicyHost-level policy triggered
AlertSecurity alert generated
LogGeneral activity log
# Configure KubeArmor to export logs
# In Helm values:
kubearmor:
relay:
exporters:
- type: elasticsearch
url: http://elasticsearch:9200
index: kubearmor-logs
- type: kafka
brokers: kafka:9092
topic: kubearmor-alerts

FeatureKubeArmorTetragonFalco
Primary ModelAllow-listingBlock-listingDetection
EnforcementYes (LSM)Yes (eBPF)No (alert only)
Policy DiscoveryYes (built-in)NoLimited
Kubernetes NativeCRDsCRDsHelm + YAML
BackendAppArmor/BPF-LSM/SELinuxeBPF onlyKernel module/eBPF
Host ProtectionYesYesYes
File IntegrityYesLimitedYes
Learning ModeYesNoNo

KubeArmor is ideal when:

  • You want default-deny security (allow-listing)
  • You need automatic policy discovery
  • Your nodes use AppArmor or SELinux
  • You want least-privilege enforcement without writing complex rules
  • You’re securing containers without modifying them

Tetragon is better when:

  • You need immediate kernel-level blocking
  • You’re already using Cilium
  • You want to block specific attack patterns

Falco is better when:

  • You need rich behavioral detection
  • You’re building a detection & response pipeline
  • You want community-maintained rule sets

An e-commerce company discovered that one of their third-party container images had been compromised. The image maintainer’s credentials were stolen, and a cryptominer was injected into the image.

The Attack Path:

  1. Malicious image pushed to public registry
  2. CI/CD pulls “latest” tag (the compromised version)
  3. Container starts, miner runs alongside legitimate app
  4. 3 weeks before detection (unusual CPU usage noticed)

The Problem:

  • Vulnerability scanning didn’t help (miner wasn’t a known CVE)
  • Runtime detection (Falco) would have alerted, but miner had already run
  • No way to know what “normal” looked like

The KubeArmor Solution:

First, they used discovery mode to understand legitimate behavior:

Terminal window
# Observe the known-good version
karmor discover --namespace production --deployment checkout-service --output checkout-policy.yaml

Then they reviewed and applied the generated policy:

apiVersion: security.kubearmor.com/v1
kind: KubeArmorPolicy
metadata:
name: checkout-service-lockdown
namespace: production
spec:
selector:
matchLabels:
app: checkout-service
process:
matchPaths:
- path: /usr/local/bin/node
action: Allow
matchDirectories:
- dir: /
recursive: true
action: Block
file:
matchDirectories:
- dir: /app/
recursive: true
action: Allow
- dir: /tmp/
action: Allow
# Default: block all other file access
network:
matchProtocols:
- protocol: tcp
action: Allow
- protocol: udp
action: Allow

The Results:

When the compromised image was deployed:

/usr/bin/xmrig
# KubeArmor logs showed:
# [BLOCKED] File write: /var/tmp/.cache/miner.conf
# [BLOCKED] Network: tcp connect to mining-pool.com:3333

The miner couldn’t:

  • Execute (not in process allow-list)
  • Write config files (outside allowed directories)
  • Connect to the mining pool (no exception for that destination)

The container ran normally because the legitimate Node.js app was in the allow-list. The attack was completely neutralized.


MistakeProblemSolution
Too restrictive initial policyBreak legitimate functionalityStart in Audit mode, then Block
Not using discovery modeManual policies miss edge casesRun discovery in staging first
Applying Block globally too soonProduction outageTest thoroughly before enforcement
Forgetting system librariesApp won’t startInclude /lib/, /usr/lib/ as needed
Not allowing /tmpMany apps need temp filesInclude /tmp/ in file allow-list
Blocking DNS UDPNetwork failuresAlways allow UDP for DNS

Hands-On Exercise: Secure a Web Application

Section titled “Hands-On Exercise: Secure a Web Application”

Objective: Use KubeArmor to create a least-privilege policy for a web application.

Terminal window
# Create test namespace
kubectl create namespace kubearmor-demo
# Deploy a simple web application
kubectl apply -n kubearmor-demo -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 1
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: web-app
spec:
selector:
app: web-app
ports:
- port: 80
EOF
# Wait for deployment
kubectl wait --for=condition=available deployment/web-app -n kubearmor-demo --timeout=60s
Terminal window
# Start monitoring KubeArmor logs for this namespace
karmor logs --namespace kubearmor-demo
# In another terminal, generate some activity
kubectl exec -n kubearmor-demo deploy/web-app -- ls /
kubectl exec -n kubearmor-demo deploy/web-app -- cat /etc/nginx/nginx.conf
kubectl exec -n kubearmor-demo deploy/web-app -- wget -q -O- http://example.com
Terminal window
# Apply a policy in Audit mode first
kubectl apply -f - <<EOF
apiVersion: security.kubearmor.com/v1
kind: KubeArmorPolicy
metadata:
name: web-app-audit
namespace: kubearmor-demo
spec:
selector:
matchLabels:
app: web-app
process:
matchPaths:
- path: /bin/sh
action: Audit
- path: /bin/bash
action: Audit
- path: /usr/bin/wget
action: Audit
file:
matchDirectories:
- dir: /etc/
recursive: true
action: Audit
EOF
# Try the same commands and watch the logs
kubectl exec -n kubearmor-demo deploy/web-app -- cat /etc/passwd
# Should see audit log entries
Terminal window
# Now apply a blocking policy
kubectl apply -f - <<EOF
apiVersion: security.kubearmor.com/v1
kind: KubeArmorPolicy
metadata:
name: web-app-hardened
namespace: kubearmor-demo
spec:
selector:
matchLabels:
app: web-app
process:
matchPaths:
- path: /usr/sbin/nginx
action: Allow
matchDirectories:
- dir: /
recursive: true
action: Block
file:
matchDirectories:
- dir: /etc/nginx/
recursive: true
readOnly: true
action: Allow
- dir: /var/log/nginx/
action: Allow
- dir: /var/cache/nginx/
action: Allow
- dir: /usr/share/nginx/html/
readOnly: true
action: Allow
matchPaths:
- path: /etc/passwd
readOnly: true
action: Allow
- path: /etc/group
readOnly: true
action: Allow
EOF
Terminal window
# Nginx should still work
kubectl exec -n kubearmor-demo deploy/web-app -- curl -s localhost
# Shell should be blocked
kubectl exec -n kubearmor-demo deploy/web-app -- sh -c "echo test"
# Should fail or be killed
# wget should be blocked
kubectl exec -n kubearmor-demo deploy/web-app -- wget -q http://example.com
# Should fail
# Reading sensitive files outside allow-list should fail
kubectl exec -n kubearmor-demo deploy/web-app -- cat /etc/shadow
# Should fail
Terminal window
# See all blocked attempts
karmor logs --namespace kubearmor-demo --logFilter=policy
# You should see entries like:
# MatchedPolicy - Block - Process /bin/sh
# MatchedPolicy - Block - File /etc/shadow
  • Observed normal activity in Audit mode
  • Applied the hardened policy
  • Verified nginx still serves requests
  • Verified shell access is blocked
  • Verified wget/curl are blocked
  • Saw blocked attempts in KubeArmor logs
Terminal window
kubectl delete namespace kubearmor-demo

What is the fundamental security model difference between KubeArmor and traditional block-lists?

Show Answer

KubeArmor uses allow-listing (default-deny) instead of block-listing (default-allow)

With allow-listing, you explicitly define what is permitted, and everything else is automatically blocked. This is more secure because new/unknown attacks are blocked by default, rather than requiring you to know about them in advance.

What enforcement backends does KubeArmor support?

Show Answer

AppArmor, BPF-LSM, and SELinux

KubeArmor automatically detects and uses the best available enforcer on each node. AppArmor is common on Ubuntu/Debian, SELinux on RHEL/CentOS, and BPF-LSM works on newer kernels (5.7+) regardless of distribution.

What is the purpose of KubeArmor’s discovery mode?

Show Answer

To automatically generate security policies by observing application behavior

Discovery mode watches what processes execute, files are accessed, and network connections are made. It then generates a KubeArmorPolicy that allows only those observed behaviors, providing a starting point for least-privilege enforcement.

What is the difference between KubeArmorPolicy and KubeArmorClusterPolicy?

Show Answer

KubeArmorPolicy is namespaced; KubeArmorClusterPolicy applies cluster-wide

KubeArmorPolicy only affects pods in its namespace. KubeArmorClusterPolicy can target pods across all namespaces, useful for enforcing security baselines like “block shell access everywhere.”

How does KubeArmor differ from Kubernetes NetworkPolicies?

Show Answer

KubeArmor enforces at the container/process level; NetworkPolicies enforce at the pod network level

KubeArmor can block specific binaries from making network connections, control file access, and manage process execution. NetworkPolicies only control pod-to-pod network traffic.

What action should you use when first deploying KubeArmor policies?

Show Answer

action: Audit

Audit mode allows the operation but logs it. This lets you verify the policy works correctly without breaking the application. Once you’re confident, switch to Block.

Why should you include /tmp in most allow-lists?

Show Answer

Many applications need to write temporary files

Applications often use /tmp for scratch space, caching, or temporary data. Blocking /tmp can cause unexpected application failures.

How do you view KubeArmor security events?

Show Answer

karmor logs command or by checking the KubeArmor relay logs

karmor logs streams security events in real-time. You can filter by namespace with --namespace or by event type with --logFilter=policy.


  1. Allow-listing beats block-listing - define what’s permitted, block everything else
  2. Discovery mode generates policies - observe first, enforce later
  3. Multiple enforcement backends - AppArmor, BPF-LSM, SELinux
  4. Start in Audit mode - verify before blocking
  5. Kubernetes-native CRDs - policies are just YAML
  6. Process + File + Network - comprehensive control
  7. Complements other tools - use alongside Falco and Tetragon
  8. No application changes - security without code modification
  9. Visibility is built-in - logs show all blocked attempts
  10. Least privilege enforced - containers only do what they need


You’ve completed the security tools toolkit! Continue to Platform Engineering Disciplines to learn how to apply these tools in practice.