DEV Community

Hugo | DevOps | Cybersecurity
Hugo | DevOps | Cybersecurity

Posted on Originally published at valtersit.com

AppArmor and seccomp Profiles: Restricting Container System Calls

AppArmor and seccomp Profiles: Restricting Container System Calls

A Senior DevSecOps Guide to Kernel-Level Container Hardening

Target Audience: Platform engineers, security engineers, Kubernetes operators

Tone: Direct, opinionated, battle-hardened

Prerequisites: Basic Linux kernel concepts, container runtime familiarity


Introduction — Why Your Containers Have Too Much Kernel Access

I've spent 15 years watching teams treat container security as a checkbox exercise. The worst part? Docker's default seccomp profile isn't a security boundary — it's a courtesy. It's designed to not break your applications, not to stop attackers.

The uncomfortable truth is that most container escapes (CVE-2022-0492, CVE-2023-25173) exploit syscalls that neither AppArmor nor seccomp block by default. Every unshare(), mount(), or keyctl() call is a potential privilege escalation vector. And most teams are running with the kernel equivalent of a screen door.

Here's a quick reality check:

# Show default seccomp profile in Docker
docker run --rm -it alpine sh -c 'cat /proc/$$/status | grep Seccomp'
# Returns: Seccomp: 2 (filtered, but you'd be surprised what's allowed)
Enter fullscreen mode Exit fullscreen mode

I once audited a production cluster where 60% of containers ran with --privileged because "it was faster than debugging permission issues." The CISO had a stroke. The CTO didn't care. We fixed it with profiles — but it took three months of convincing.

Here's the actionable takeaway: If you're not running custom AppArmor and seccomp profiles, you're running with training wheels on a motorcycle. One wrong turn and you're eating pavement.

:::note[TL;DR]

  • Default container profiles allow 300+ syscalls — most workloads need <50
  • AppArmor blocks file paths, seccomp blocks syscalls — you need both
  • Capabilities don't replace profiles; they complement them
  • Kubernetes doesn't enforce profile content, only existence — use admission controllers
  • Test profiles in complain/log mode for 24+ hours before enforcing :::

Prerequisites

Before we dive in, you'll need:

  • A Linux system with AppArmor enabled (check with sudo aa-status)
  • Docker or containerd runtime (v20.10+ for full seccomp support)
  • strace, auditd, and apparmor-utils installed
  • Kubernetes cluster (v1.25+) if deploying profiles to K8s
  • Basic familiarity with kernel syscalls and security contexts

Understanding the Linux Syscall Attack Surface in Containers

The 300+ Syscalls Your Container Doesn't Need

The problem is straightforward: default profiles allow 300+ syscalls. Most containers need fewer than 50. Every extra syscall is a potential weapon for an attacker who gets code execution inside your container.

Consider unshare(CLONE_NEWNS) — a single syscall that can break out of mount namespaces. A good seccomp profile blocks it. The default Docker profile? It allows it.

Here's how to see what your container actually calls:

# Capture syscalls from a running container
docker run --rm -it --security-opt seccomp=unconfined alpine sh -c "strace -c -S name ls / 2>&1 | tail -20"
Enter fullscreen mode Exit fullscreen mode

This runs ls with strace and shows a syscall count. You'll see maybe 30 syscalls for a simple command. Now imagine what a web server or database needs — probably 40-60, not 300.

Comparison: Default Docker profile vs. minimal web server profile


⚠️ DECLASSIFIED / TRUNCATED VERSION
You are reading a truncated version of this technical guide.
To read the full, unedited deep-dive (including all configuration files, architecture diagrams, and high-res images), visit the original post on Valters IT Docs.

Top comments (0)