kapparmor

Build Status CodeQL Analysis Go Report Card codecov OpenSSF Best Practices OpenSSF Scorecard


kapparmor logo Kapparmor

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.

Table of Contents


Overview

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.


Why AppArmor?

AppArmor vs SELinux vs Seccomp

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:


Key Features

πŸ” Enterprise-Grade Security

⚑ Kubernetes-Native

πŸ›‘οΈ Robust Profile Management

πŸ“ˆ Production-Ready


Security-First Approach

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.


Getting Started

Prerequisites

System Requirements:

Verify AppArmor is enabled:

# On each node
sudo aa-status

# Expected output shows: "X profiles loaded" and "X processes are in enforce/complain mode"

Installation

# 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

Via kubectl (Manual)

kubectl apply -f https://github.com/tuxerrante/kapparmor/releases/download/v1.0.0/kapparmor-manifest.yaml

Quick Start

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"

Architecture

How It Works

  1. Polling – Every POLL_TIME seconds (default: 30s), Kapparmor checks the kapparmor-profiles ConfigMap
  2. Comparison – Identifies new, modified, or deleted profiles by comparing with local state
  3. Validation – Validates profile syntax before kernel loading:
    • Profile name must start with custom.
    • Filename must match profile name
    • Must contain profile keyword and opening brace {
    • Path traversal checks on filename
  4. Loading – Executes apparmor_parser --replace <profile> for new/updated profiles
  5. Unloading – Executes apparmor_parser --remove <profile> for deleted profiles
  6. Cleanup – Removes profile files from /etc/apparmor.d/custom/

Component Diagram

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   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/      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Configuration

Environment Variables / Helm Values

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

Helm Chart Values Example

# 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

Constraints & Limitations

⚠️ Important:

  1. 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)
    
  2. Profile Syntax – Profiles must be valid AppArmor syntax:
    βœ… REQUIRED: profile custom.name { ... }
    ❌ NOT SUPPORTED: hat name { ... } (nested profiles)
    
  3. Polling Interval – Must be between 1 and 86400 seconds (24 hours)

  4. 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
    
  5. 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"}]'
    

Testing

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.


Documentation

πŸ“š Available Documentation

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)

πŸ”— External References

πŸ“– Learning Resources


Release Process

  1. ✏️ Update config/config with new versions (app, chart, Go)
  2. ✏️ Update charts/kapparmor/Chart.yaml with matching version
  3. πŸ§ͺ Run unit and integration tests (see Makefile)
  4. ✏️ Update charts/kapparmor/CHANGELOG.md
  5. πŸ“ Open PR, get reviews
  6. βœ… Merge to main
  7. 🏷️ Create signed Git tag: git tag -s v1.0.0
  8. πŸš€ GitHub Actions automatically builds and publishes

Note: Commits must be signed (git config commit.gpgsign true)


Community & Support


License

This project is licensed under the Apache 2.0 License.


Credits & Acknowledgments


Made with ❀️ for cloud-native security