KapparmorDynamic AppArmor Profile Management for Kubernetes
Kapparmor is a cloud-native security enforcer that simplifies AppArmor profile management in Kubernetes clusters. Deploy, update, and manage AppArmor security profiles across your infrastructure through a simple ConfigMap interfaceβno manual node configuration required.

Kapparmor dynamically loads and unloads AppArmor security profiles on Kubernetes cluster nodes via ConfigMap. It runs as a privileged DaemonSet on Linux nodes, eliminating the need for manual profile management on each node.
Key Capabilities:
This work was inspired by kubernetes/apparmor-loader.
| Feature | AppArmor | SELinux | Seccomp |
|---|---|---|---|
| Type | MAC (Mandatory Access Control) | MAC | Syscall filtering |
| Scope | File access, capabilities, networking | File access, labels | System calls only |
| Learning Curve | π’ Easy (plain-text profiles) | π΄ Steep (complex contexts) | π’ Simple (syscall lists) |
| Maintenance | π’ Low (profile-per-app) | π‘ Medium (policy system) | π‘ Medium (tool-dependent) |
| Kubernetes Support | β Native via AppArmor | β Via labels | β Native (RuntimeDefault) |
| Use Case | Container workloads | Enterprise systems | Syscall restriction |
Choose AppArmor when you need:
Choose SELinux when you need:
Choose Seccomp when you need:
π Enterprise-Grade Security
β‘ Kubernetes-Native
π‘οΈ Robust Profile Management
π Production-Ready
Kapparmor is built with security as a core principle:
β
Threat Modeling β Comprehensive STRIDE analysis
β
Code Quality β 80%+ test coverage, zero high-severity CodeQL alerts
β
Supply Chain β Pinned dependencies, signed commits, SBOM tracking
β
Vulnerability Scanning β Trivy, Gosec, Snyk integration
β
Least Privilege β Minimal RBAC, no elevated capabilities unless required
π Read the full security threat model for detailed analysis of risks and mitigations.
System Requirements:
cat /sys/module/apparmor/parameters/enabled
# Output should be: Y
Verify AppArmor is enabled:
# On each node
sudo aa-status
# Expected output shows: "X profiles loaded" and "X processes are in enforce/complain mode"
# Add the Kapparmor Helm repository
helm repo add tuxerrante https://tuxerrante.github.io/kapparmor
helm repo update
# Install with defaults
helm upgrade kapparmor --install \
--namespace kube-system \
--atomic \
--timeout 120s \
tuxerrante/kapparmor
# Or customize values
helm upgrade kapparmor --install \
--namespace kube-system \
--set image.tag=v1.0.0 \
--set app.pollTime=30 \
tuxerrante/kapparmor
kubectl apply -f https://github.com/tuxerrante/kapparmor/releases/download/v1.0.0/kapparmor-manifest.yaml
1. Create an AppArmor profile ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: kapparmor-profiles
namespace: kube-system
data:
custom.deny-write-outside-home: |
#include <tunables/global>
profile custom.deny-write-outside-home flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
capability setuid,
capability setgid,
capability dac_override,
/home/** rw,
/tmp/** rw,
/var/tmp/** rw,
deny /etc/** w,
deny /root/** w,
deny / w,
}
2. Apply the ConfigMap:
kubectl apply -f apparmor-profiles.yaml
3. Deploy workload with the profile:
apiVersion: v1
kind: Pod
metadata:
name: secure-app
annotations:
container.apparmor.security.beta.kubernetes.io/app: localhost/custom.deny-write-outside-home
spec:
containers:
- name: app
image: ubuntu:24.04
command: ["/bin/bash", "-c", "sleep infinity"]
4. Verify profile was loaded:
# Check on the node
sudo aa-status | grep custom.deny-write-outside-home
# Or from the pod
kubectl logs -n kube-system -l app=kapparmor | grep "Profile.*loaded"
POLL_TIME seconds (default: 30s), Kapparmor checks the kapparmor-profiles ConfigMapcustom.profile keyword and opening brace {apparmor_parser --replace <profile> for new/updated profilesapparmor_parser --remove <profile> for deleted profiles/etc/apparmor.d/custom/ββββββββββββββββββββββββββββββββββββββββ
β Kubernetes Control Plane β
β (ConfigMap: kapparmor-profiles) β
ββββββββββββββ¬ββββββββββββββββββββββββββ
β
β (mount via volume)
βΌ
ββββββββββββββββββββββββββββββββββββββββ
β Kapparmor DaemonSet Pod β
β ββββββββββββββββββββββββββββββββββ β
β β Poll ConfigMap every 30s β β
β β Validate profiles β β
β β Copy to /etc/apparmor.d/custom β β
β β Execute apparmor_parser β β
β ββββββββββββββββββββββββββββββββββ β
ββββββββββββββ¬ββββββββββββββββββββββββββ
β
β (apparmor_parser binary)
βΌ
ββββββββββββββββββββββββββββββββββββββββ
β Host Linux Kernel β
β (AppArmor module) β
β /sys/kernel/security/apparmor/ β
ββββββββββββββββββββββββββββββββββββββββ
| Parameter | Default | Description |
|---|---|---|
app.pollTime |
30 |
Polling interval in seconds (1-86400) |
app.configmapPath |
/app/profiles |
ConfigMap mount path |
app.profilesDir |
/etc/apparmor.d/custom |
Host directory for profiles |
image.repository |
ghcr.io/tuxerrante/kapparmor |
Container image |
image.tag |
latest |
Image tag/version |
resources.limits.cpu |
200m |
CPU limit per pod |
resources.limits.memory |
128Mi |
Memory limit per pod |
# values.yaml
app:
pollTime: 30
configmapPath: /app/profiles
profilesDir: /etc/apparmor.d/custom
logLevel: "INFO"
image:
repository: ghcr.io/tuxerrante/kapparmor
tag: "v1.0.0"
pullPolicy: IfNotPresent
resources:
limits:
cpu: 200m
memory: 128Mi
requests:
cpu: 100m
memory: 64Mi
nodeSelector:
kubernetes.io/os: linux
β οΈ Important:
custom. prefix and match the filename
β BAD: myprofile (missing prefix)
β
GOOD: custom.myprofile (filename must also be custom.myprofile)
β
REQUIRED: profile custom.name { ... }
β NOT SUPPORTED: hat name { ... } (nested profiles)
Polling Interval β Must be between 1 and 86400 seconds (24 hours)
# Cleanup before initial deployment
sudo rm -f /etc/apparmor.d/custom/*
sudo systemctl reload apparmor
# BAD: This can crash Kapparmor
kubectl delete configmap kapparmor-profiles
# GOOD: Delete pods first
kubectl delete pod -l app-profile=myprofile
kubectl patch configmap kapparmor-profiles --type json -p='[{"op":"remove","path":"/data/custom.myprofile"}]'
Comprehensive testing is documented in docs/testing.md.
Quick test:
# Run Go tests
make test
# Run security checks
make lint
# Deploy to local MicroK8s cluster (if available)
./build/test_on_microk8s.sh
See the KAppArmor Demo project for practical examples.
| Document | Purpose |
|---|---|
| ThreatModel.md | Complete security threat model (STRIDE analysis, risk assessment, mitigations) |
| testing.md | Testing strategies and local cluster setup |
| microk8s.md | MicroK8s-specific deployment guide |
| kapparmor-architecture.drawio | Architecture diagrams (editable Drawio format) |
complain mode for testing before enabling enforce modeconfig/config with new versions (app, chart, Go)charts/kapparmor/Chart.yaml with matching versionMakefile)charts/kapparmor/CHANGELOG.mdgit tag -s v1.0.0Note: Commits must be signed (git config commit.gpgsign true)
This project is licensed under the Apache 2.0 License.
Made with β€οΈ for cloud-native security