Dynamic 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.
- Why AppArmor?
- Key Features
- Security-First Approach
- Getting Started
- Architecture
- Configuration
- Constraints & Limitations
- Testing
- Documentation
- Release Process
- Contributing
- Community & Support
- License
- Credits & Acknowledgments
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:
- π Dynamic Loading β Apply profile changes without node restarts
- π¦ ConfigMap-Based β Version control your security policies as Kubernetes manifests
- π§Ή Auto-Cleanup β Automatically remove unused profiles
- π Change Detection β Detects and syncs profile modifications
- β Validation β Validates syntax before kernel loading
- π Observable β Health endpoints and structured logging
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:
- Easy-to-understand security profiles
- File and path-level access control
- Capability restrictions
- Port binding restrictions
- Network namespace access control
Choose SELinux when you need:
- Label-based context systems
- Enterprise policy frameworks (CIS profiles)
- Existing infrastructure investment
Choose Seccomp when you need:
- Only syscall filtering
- Lightweight containerized defaults
- Minimal overhead for simple restrictions
π Enterprise-Grade Security
- Input validation with fuzz testing
- Secure coding practices (SSDLC)
- Supply chain security (signed commits, Harden-Runner, CodeQL)
- Zero external runtime dependencies
β‘ Kubernetes-Native
- DaemonSet-based deployment
- ConfigMap-driven configuration
- Health checks and readiness probes
- Optional Prometheus metrics
π‘οΈ Robust Profile Management
- Syntax validation before loading
- Filename/profile name consistency checks
- Path traversal protection
- Automatic cleanup of orphaned profiles
π Production-Ready
- Comprehensive test coverage
- CI/CD security gates
- OpenSSF Best Practices certified
- No privileged escalation vectors
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:
- Kubernetes 1.23+
- Ubuntu 22.04+ or similar Debian-based Linux nodes
- AppArmor enabled on all nodes:
cat /sys/module/apparmor/parameters/enabled # Output should be: Y - Helm 3.0+ (for easy installation)
Verify AppArmor is enabled:
# On each node
sudo aa-status
# Expected output shows: "X profiles loaded" and "X processes are in enforce/complain mode"# Install directly from ghcr.io (no helm repo add needed)
helm upgrade kapparmor --install \
--namespace kube-system \
--atomic \
--timeout 120s \
oci://ghcr.io/tuxerrante/charts/kapparmor
# Or customize values
helm upgrade kapparmor --install \
--namespace kube-system \
--set image.tag=v1.0.0 \
--set app.pollTime=30 \
oci://ghcr.io/tuxerrante/charts/kapparmor --version 0.3.1# 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/kapparmorkubectl apply -f https://github.com/tuxerrante/kapparmor/releases/download/v1.0.0/kapparmor-manifest.yaml1. 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.yaml3. 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"- Polling β Every
POLL_TIMEseconds (default: 30s), Kapparmor checks thekapparmor-profilesConfigMap - Comparison β Identifies new, modified, or deleted profiles by comparing with local state
- Validation β Validates profile syntax before kernel loading:
- Profile name must start with
custom. - Filename must match profile name
- Must contain
profilekeyword and opening brace{ - Path traversal checks on filename
- Profile name must start with
- Loading β Executes
apparmor_parser --replace <profile>for new/updated profiles - Unloading β Executes
apparmor_parser --remove <profile>for deleted profiles - Cleanup β Removes profile files from
/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-
Profile Naming β Custom profiles MUST start with
custom.prefix and match the filenameβ BAD: myprofile (missing prefix) β GOOD: custom.myprofile (filename must also be custom.myprofile) -
Profile Syntax β Profiles must be valid AppArmor syntax:
β REQUIRED: profile custom.name { ... } β NOT SUPPORTED: hat name { ... } (nested profiles) -
Polling Interval β Must be between 1 and 86400 seconds (24 hours)
-
Node State β Start on clean nodes (remove old orphaned profiles first)
# Cleanup before initial deployment sudo rm -f /etc/apparmor.d/custom/* sudo systemctl reload apparmor
-
Pod Dependencies β Always delete pods using a profile before removing the profile from ConfigMap
# 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.shSee 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) |
- Kubernetes AppArmor Tutorial β Official K8s guide
- AppArmor Documentation β Ubuntu reference
- AppArmor Profile Reference β Complete profile syntax
- AppArmor Profiles β SUSE documentation
- AppArmor Profiles are easier to learn than SELinux policies and more flexible than Seccomp
- Start with simple restrictive profiles (deny certain paths/capabilities)
- Use
complainmode for testing before enablingenforcemode - The included sample profiles are good starting points
- βοΈ Update
config/configwith new versions (app, chart, Go) - βοΈ Update
charts/kapparmor/Chart.yamlwith matching version - π§ͺ Run unit and integration tests (see
Makefile) - βοΈ Update
charts/kapparmor/CHANGELOG.md - π Open PR, get reviews
- β Merge to main
- π·οΈ Create signed Git tag:
git tag -s v1.0.0 - π GitHub Actions automatically builds and publishes
Note: Commits must be signed (git config commit.gpgsign true)
Contributions are welcome! Please read CONTRIBUTING.md for:
- How to report bugs and request features
- How to set up a development environment
- Coding standards and testing requirements
- The pull request process
This project follows the Contributor Covenant Code of Conduct.
For security vulnerabilities, see SECURITY.md.
- π Found a bug? Open an issue
- π‘ Feature request? Start a discussion
- π Need help? Check the docs
- π Changelog: See CHANGELOG.md for release history
This project is licensed under the Apache 2.0 License.
- π¨ Logo design by @Noblesix960
- π Inspired by kubernetes/apparmor-loader
- π Security guidance from Microsoft SDL and OWASP
- βοΈ Cloud-native architecture patterns from CNCF ecosystem
Made with β€οΈ for cloud-native security